Add new with-type binding that is useful for tracking non-identity changes #1811

Merged
merged 2 commits into from Dec 6, 2016

Projects

None yet

3 participants

@mbest
Member
mbest commented Jun 17, 2015

Bindings such as with, foreach, and component create a new binding context and need to refresh that context when the viewmodel changes. When 3.0.0 introduced support for observable viewmodels, it became possible to do this refresh without destroying and re-creating the DOM, but still most bindings don't use it.

Of course, custom bindings (for example, withlight) can use the observable viewmodel feature, allowing the user the possibly of some control over whether a VM change requires a full DOM refresh, but I think this may be something that can be integrated into the existing bindings with the use of some sort of viewmodel identity information. Using this information, the with binding, for example, could use either of the two refresh method based on whether the identity has changed:

  • The viewmodel has changed, but has the same identity as the previous one: update the DOM without re-creation (update each binding within the child context).
  • The viewmodel has changed to a new identity (the default), re-create the DOM and re-apply bindings.
@mbest mbest added this to the Post 3.4.0 milestone Nov 20, 2015
@mbest mbest changed the title from Distinguish between identity and non-identity VM changes to Add new with-type binding that is useful for tracking non-identity changes Nov 20, 2015
@mbest mbest referenced this pull request Nov 20, 2015
Open

Bindings to add/enhance #1932

mbest added some commits Nov 20, 2015
@mbest mbest Add "using" binding (formerly called withlight). f07616a
@mbest mbest When using contains, compare the parent if not an element.
One of the "using" tests failed in PhantomJS because PhantomJS doesn't support "contains" for comments.
7785a44
@mbest
Member
mbest commented Nov 20, 2015

One of the using tests failed only in PhantomJS. By spec contains is only for elements, although most browsers seem to work fine if it's called for a comment node. But not PhantomJS. I'm surprised this hadn't been exposed by any other test.

@ThomasMichon

For a moment, I thought this was identical to the new let binding, before I noticed that the let binding uses bindingContext['extend'] and the using binding uses bindingContext['createChildContext'].

One thing that concerns me is that I feel that the with and using bindings have their usage patterns swapped: the with binding should simply let me reduce the scope for my current binding context, but not cause costly re-binding, as the with operator in JavaScript has a similar usage, but the using binding sounds like it should create and destroy a local scope each time its source value changes. I realize there's not much that can be done about that due to momentum of the current behavior, but it's something which will require a concise explanation in documentation. A lot of my teammates blindly use the with binding when they really meant to use an equivalent to the using or let bindings.

You mention integrating the using binding "into the existing bindings". How would that be accomplished? One of the more irritating scenarios in Knockout right now is the case where you want to conditionally render a template or component:

<!-- ko if: person -->
<!-- ko component: { name: 'contact-info', params: { person: person } } -->
<!-- /ko -->
<!-- /ko -->

Above, the template author did not bother to implement the contact-info control in a way which handles a null person, but it is possible that the person observable notifies the component binding with a null value before it notifies the if binding. This will cause a binding error.

A solution is this:

<!-- ko with: { data: person } -->
<!-- ko component: { name: 'contact-info', params: { person: $data } } -->
<!-- /ko -->
<!-- /ko -->

Here, when the person observable changes, the with binding re-binds, throwing out the old component completely and rendering it again with the new value. If person is ever null, nothing is re-rendered, and the current component is disposed before its bound value changes. This prevents the bug, but does not allow you to re-use the contact-info component.

It seems like we really need a new binding which lets you re-use a binding context while a condition is true, but allow you to re-bind when the value changes and avoid passing down unwanted values to your rendered template. I am not sure how the using or let bindings help with this scenario so far.

@mbest
Member
mbest commented Dec 5, 2015

@ThomasMichon You're right that using or let won't help in the scenario you presented because neither of them save the child nodes to be able to clear and later display/rebind them.

In your example of using the if binding, you don't need to worry about the notification order because Knockout always notifies bindings in the order they were bound. It will notify the if binding first, which will remove and dispose the component before it ever gets a notification.

@brianmhunt brianmhunt added a commit to knockout/tko.utils that referenced this pull request Oct 3, 2016
@brianmhunt brianmhunt Update `isContainedBy` re. knockout/knockout#1811 1c5fa2b
@brianmhunt brianmhunt added a commit to knockout/tko.binding.core that referenced this pull request Oct 3, 2016
@brianmhunt brianmhunt Incorporate the `using` binding 560de37
@mbest mbest merged commit 0a35a80 into master Dec 6, 2016

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment