If.bind needs explanation

If I have a custom element wrapped inside an if.bind and I trigger the if.bind to conditionally render the control the constructor of the custom element is called the 1st time this is done.

If I then hide the control by triggering the if.bind again and then after that make it visible again the constructor is not called

When the custom element is unloaded it calls
Detached

When custom element is made visible for the 2nd time it calls
Attached

It does not call the constructor or bind etc the 2nd time

What is the expected behaviour here

Additionally when the component is made visible for the second time it causes the following issues
repeat.for does not refresh
this.functionName does not work when it was setup from a parent custom element using call binding

I have found an easier way to create this issue

Having 1 if.bind on the parent (

) and another in the child element (
)

Breaks the repeater is this the expected behaviour

How I understand if.bind vs show.bind is that if will remove from the DOM where show toggles the aurelia-hide CSS class (see http://aurelia.io/docs/templating/basics#conditionals). So if you are showing/hiding and not either show or hide once, then use show.bind.

Hi Rkever

Thanks for your response

I need to use if.bind because I don’t want to render everything on load in a hidden state for performance reasons.

Additionally it seems my approach should work

But yes I have tried if.bind which does alleviate some issues but will likely cause a performance issue later

There is also the with condition that works well with this type of scenario when a model is passed into the component. But yes, I see show would for sure not work for your case.

Tempted to say this is a bug

It would be nice to confirm whether this is a bug or not

I’m not sure if that is a bug or not but you are correct, all the lifecycle methods do fire with the exception of the constructor. I suppose what’s happening is that they are not new’uping the class and just using what’s in memory (maybe for performance?) but still invoke the lifecycle. Perhaps putting those type of models/variables within your constructor is not the best place. Placing them in say the attached() or bind() might be a better place.

BTW I did confirm bind() does fire when toggling if.bind, constructor does not.

Yes could be for performance it works like that

Although judging by my more simple example code the constructor not being called appears to be irrelevant it seems the issue is entirely down to nested if.binds

Ok I have found what the issue is

If you have nested if.binds they need to be set or unset in the correct order

I found changing the variable used in the if.bind to undefined in detached solves the issue

I was setting them in the correct order but not unasigning them in the correct order

<div if.bind='show'>
    <div if.bind='show2'>
        <div repeat.for="row of rows">
            ${row.name}
        </div>
        <div>
            test ${rows[0].name}
        </div>
    </div>
</div>
<button click.delegate="showRows()">
</button>

The code example above is all you need to recreate this issue in my case the scenario is more complex but basically the same.

The issue will happen if I use the same variable for both if.binds or I trigger them in the wrong order, I would have thought the inner if.bind should not even be allowed to trigger until the outside one has but clearly not

@Infuser, could you test it against old version of if.bind implementation?
You can just fix the version to old "aurelia-templating-resources": "1.4.0", in your package.json file.

If this problem only appears with latest aurelia-templating-resources 1.5.x, you’d better create an issue on https://github.com/aurelia/templating-resources

I believe this issue does exist in older versions but I will have a go

In the end I decided playing with the ordering of setting if.bind variables is not a viable solution as I believe it would be to prone to being broken again additionally I use if.bind a lot.

I have decided what is needed is a best of both worlds approach, don’t render this until x happens and then toggle visibilty when y happens. In this way nothing unneeded is rendered until required and toggling visibility is controlled by show.bind.

I did have to add an extra div container around the content to facilitate this as you cannot have 2 controllers on one element