Canjs 3.2.1 can-component inserted event

I am wondering on how to get the inserted event so that I can manipulate the element with jQuery once the component is loaded.

Thanks!

Nevermind, that has not changed from 2.*

var AuthLogin = Component({
    tag: 'page-auth-login',
    view: loginTemplate,
    ViewModel: AuthLoginViewModel,
    events: {
        inserted: function(){
            console.log( 3333 );
        }
    }
});

Sorry for the post I don’t think I can delete it.

The only difference is that the inserted and removed events are now asynchronous in Can 3.x. Check out the section Asynchronous inserted/removed events on the migration guide.

Thanks for the heads up!

I have an {{#if init_loaded}} wrapped around the whole template, that is default to false. In the init event I am using a model to get some data, and then once the data is loaded I set the init_loaded variable to true,

I have to call taskGroupTemplate(); before jQuery can find an element, inside the {{#if init_loaded}}. Otherwise the element that jQuery is looking for never exists because the template is not processed and re-rendered yet.

Is this proper?

taskGroupTemplate is the view, imported via StealJs. It is used as the main view for the Component.

inserted: function( element ){
            this.viewModel.on('init_loaded', function(ev, newVal, oldVal) {
                if( newVal ) {
                    taskGroupTemplate();
                    console.log( $( this.element ).find( '.someitem' ) );
                }
            }.bind( this ));
        }

Thanks!

The code above is what you’re using to work around the issue, is that correct?

It would be helpful to see what the code looked like when it was not working. If you could create a JSBin that would be the easiest way to debug, but posting the non-working code here would work also.

Yeah, it is what I am using to get around the issue.

Here is the JSbin:

You can see that the console.log( this.element.querySelector( ‘#test’ ) ); returns null if the MyComponentTemplate(); function is not called.

setTimeout is to simulate an ajax call.

var MyMap = can.DefineMap.extend('MyMap', {
  init_loaded: {
    value: false
  },
});

var MyComponentTemplate = can.stache( '{{#if init_loaded}}<div id="test">test element</div>{{/if}}' );

var MyComponent = can.Component.extend({
    tag: 'my-component',
    viewModel: MyMap,
    view: MyComponentTemplate,
    events: {
      init: function(){
        //this.viewModel.init_loaded = true;
        setTimeout(function(){
          this.viewModel.init_loaded = true;
        }.bind( this ), 2000);
      },
      inserted: function( element ){
        if( this.viewModel.init_loaded ) {
          //console.log( this.element.getElementById( 'test' ).style.color = "red" );
          //console.log( this.element.querySelector( '#test' ) );
        }
        this.viewModel.on( 'init_loaded', function( ev, newVal, oldVal ){
          if( newVal ) {
              //MyComponentTemplate();
              console.log( this.element.querySelector( '#test' ) );
          }
        }.bind( this ));
      }
    }
});

var MyRender = can.stache( '<my-component/>' );
var fragment = MyRender(  )
document.body.appendChild(fragment)

I must be missing something.

Thanks!

Ok, I see what you’re saying now. When init_loaded changes, this handler

this.viewModel.on( 'init_loaded', function( ev, newVal, oldVal ){ ... });

is triggered before stache rerenders and adds your #test element to the DOM. Calling MyComponentTemplate(); just adds a delay so that the DOM can be updated before running your code.

There are a couple options I can think of for fixing this.

1. Use a setTimeout around your code within the handler

        this.viewModel.on( 'init_loaded', function( ev, newVal, oldVal ){
          if( newVal ) {
            setTimeout(function() {
              console.log( this.element.querySelector( '#test' ) );
            }.bind(this));  
          }
        }.bind( this ));

This will allow the template to be rendered before that querySelector runs, but makes the code more confusing and less testable.

2. You can add an ($inserted) handler to the #test element directly, so your template will look something like:

{{#if init_loaded}}
    <div id="test" ($inserted)="testInserted(%element)">test element</div>
{{/if}}

3. The best solution is probably to make your <div id="test"> element its own component so you can handle the code in the inserted handler of that component.

Thanks, I’ll probably go with #2 since it is the easiest to implement.

I just tried it, and it works perfectly!