Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

One-way bindings + shallow watching #14039

Closed
thorn0 opened this issue Feb 14, 2016 · 19 comments
Closed

One-way bindings + shallow watching #14039

thorn0 opened this issue Feb 14, 2016 · 19 comments
Assignees
Milestone

Comments

@thorn0
Copy link
Contributor

thorn0 commented Feb 14, 2016

When the PR #13928 (one-way bindings) was discussed, the shallow watching (using $watchCollection) wasn't implemented as it wasn't clear what the use case is. Seems like I've found a use case.

Suppose we have a component:

app.component('myList', {
  template: '...',
  bindings: { items: '<' }
});

and it's used like this:

<my-list items="items|orderBy:'title'"></my-list>

Can you see what's going on here? The orderBy filter returns a new array every time it's called, so we get $rootScope:infdig. Plunker.

Changing the type of the binding to =* makes it work.

@jbedard
Copy link
Collaborator

jbedard commented Feb 14, 2016

I was recently looking into this and wondering if this could be done by changing how $parse watchers work instead of resorting to something such as =*. Object/array literals have the same problem as filters. Both cases already return the same instance if all expression inputs are simple and have not changed so one option would be expanding that to somehow work with object/array inputs.

@thorn0
Copy link
Contributor Author

thorn0 commented Feb 15, 2016

Can we probably somehow reuse the array returned from the filter when the identity of the expression result doesn't change?

@Narretz Narretz added this to the Ice Box milestone Feb 15, 2016
@Narretz
Copy link
Contributor

Narretz commented Feb 15, 2016

Yes, this came up in the issue after we decided on the simple implementation. It was suggested that shallow watching had been introduced exactly for that purpose, so imo we can port it to one-way bindings. A general solution be interesting too.

@adamreisnz
Copy link

I think I have another use case for the need of shallow watching objects for one way bindings. For example when using moment.js, and passing a moment object as a binding to some component, it will not update if the moment date is changed externally, because the object reference doesn't change.

@adamreisnz
Copy link

Another use case is binding a "user" object to a certain component, which uses several of the user's properties to create an avatar, for example their initials, color, profile photo, etc. If any of those properties changes externally, the changes need to be reflected in the component. However, the component itself doesn't need to make changes to the object. Hence the need for a one way binding with a shallow watch.

@adamreisnz
Copy link

A third use case is when watching an array. When adding elements to or removing elements from an array which is bound to a component via one-way binding, the $onChanges handler doesn't fire.

@petebacondarwin
Copy link
Contributor

Would it be acceptable to implement <* that would be a one-way binding that uses $watchCollection instead of a shallow $watch to trigger $onChanges?

@adamreisnz
Copy link

That will work for arrays, but still not for objects right? Is there a technical difficulty with implementing a shallow watch for one way bindings?

I use moments a lot for a very date & time operations heavy app, and not being able to detect shallow changes on the moment object is holding me back from embracing the new component style of development.

@petebacondarwin
Copy link
Contributor

petebacondarwin commented Jun 6, 2016

It sould work for shallow comparison of objects too. See https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection

@petebacondarwin petebacondarwin modified the milestones: Backlog, Ice Box Jun 6, 2016
@adamreisnz
Copy link

Sorry you're right, I got confused yesterday.

Yes the equivalent of a =* for shallow watching of one-way bindings via <* using $watchCollection would be great, thanks!

@adamreisnz
Copy link

Hi @petebacondarwin, any idea in which Angular version this feature will land?

@petebacondarwin
Copy link
Contributor

Week have now implemented $doCheck which supports these use cases. Can you take a look?

@adamreisnz
Copy link

adamreisnz commented Jul 22, 2016

Unless I am understanding it wrong, it seems that using $doCheck would require a lot of unnecessary boilerplate code that a shallow watch of one way bindings would simplify.

For example, to watch if array elements changed, I would have to introduce the following into my code:

  • A $doCheck method in addition to existing $onChanges method
  • A local variable to keep track of previous values
  • A helper function to compare the two arrays

A shallow watch would make this much simpler as it would simply call $onChanges if needed and not require any of the above.

I currently already have work arounds for the above use cases in place, so it seems $doCheck would just be a different work around but not really elegant either.

Is it possible to still implement <* bindings? What would be the work involved?
It already exists for two way bindings, so I don't think it would be too complicated to add?

@petebacondarwin
Copy link
Contributor

I agree that it would not be much work. My only concern is that it is deviating from the way that Angular 2 does things, which is not ideal... What would you do in Angular 2 for these use cases?

@adamreisnz
Copy link

adamreisnz commented Jul 22, 2016

I don't know, good question -- I haven't had to handle that yet in an Angular 2 project.

But my argument for Angular 1 would be that if Angular already has the mechanics and internal logic in place to be able to shallow watch for changes, then it would probably be more efficient if we can hook in to those tools and use them rather than try to hack our own solution.

Moreover, there's much more stuff in Angular 1 that isn't present in Angular 2, and Angular 1 started out as a very two-way binding oriented framework. I feel it would be a wrong approach to avoid adding features that make Angular 1 development easier, just because Angular 2 doesn't have something similar.

The whole development methodology in Angular 2 in general is quite different, using mostly one-way reactive data flow, observables, classes, types etc. So while I appreciate you want to make the migration path easier, I think there's no need to strive for a 100% one-to-one feature mapping, because of these differences.

I'll try to play around with it in Angular 2 next week and see what I come up with.

@CarterLi
Copy link

Any updates?

@gkalpak
Copy link
Member

gkalpak commented Jan 25, 2018

We might consider it (given #15874 (comment)).
Does anyone want to take a stab at it?

@Narretz Narretz modified the milestones: 1.6.x, 1.7.x Apr 12, 2018
@jbedard jbedard self-assigned this May 3, 2018
@Narretz Narretz self-assigned this May 3, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 4, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 5, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 5, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 5, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 9, 2018
jbedard added a commit to jbedard/angular.js that referenced this issue May 17, 2018
@adamreisnz
Copy link

Awesome, thanks team! 🎉

@CarterLi
Copy link

CarterLi commented May 18, 2018

How can I use it in controller binding? @Narretz

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants