-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot observe changes to nested properties on array using @each #541
Comments
I don't think that's actually true... It uses the normal chain infrastructure and should work arbitrarily many levels deep. |
@wycats This sounds like a bug then. I've never seen it work more than one level deep. |
Agreed, this sounds like a bug. I believe this was working at some point. Thank you for the unit test. |
So I've been puzzling through this. It seems to boil down to EachProxy handling setting up the observers internally when you call addObserver with itself as the root. When as part of a chained path, however, the ChainNode plucks the top-level keyname off of the EachProxy, and goes on its merry way, and the EachProxy never gets involved. Basically, there are two levels of arrays at play. watching '@[email protected]' DOES work, in this instance. |
Forgot to clarify that when the ChainNode plucks the keyname off the EachProxy, it's getting an EachArray, hence the need for nesting the @ each observers. |
The way chains work is each chain node has a corresponding chainWatcher that watches the key on the node's parent's value(), in order for this to work the chain needs to stop at @each and setup the chainWatcher with the remaining path. I'm considering special casing @ character to just mean this. |
@kselden, does your |
Looks like this is still an issue. |
chains do not chain through properties that produce arrays, this requires some changes I stated above, no I haven't done any work on it. It also is problematic @each itself changes when the array does. |
I've hacked around this, for anyone that is facing this issue (especially when using ember-data associations), by adding a property on one side of the association that checks the property in the other end of the association (in my case, isLoaded) and then using that after the |
@tpitale: could you show an example of such hack? I'm not sure what you mean by adding a property on other side of association. @kselden @wagenet: I encountered something similar recently, but frankly I'm not sure if this is the same bug, could you take a look at this fiddle: http://jsfiddle.net/uErrd/4/ ? |
@drogus given the original example, there would be a nestIsDone property for the path nest.isDone. So you could observe |
would really love to see this in 1.0 not 1.1 👍 |
@mspisars Me too, but I don't want to block 1.0 on things that are difficult to fix and non-essential. |
last activity 4 months ago ... 👍 |
@mspisars, @darthdeus I would love to see it in 1.0 too. The issue is not about desire, it's about ability. |
👍 for 1.0: would be really useful to have this working. |
👍 for 1.0 |
Public Service Announcement: +1s do not provide development resources where they are lacking. We've already agreed that we'd love to have this feature but it's very much non-trivial to support. It's certainly not something we'll block 1.0 on. |
Agreed with @wagenet these are non-trivial code paths that touch mission critical parts of ember. |
Any updates on this? We have been trying to use this for a web app that's about to be used by a major Hotel chain and none of the workarounds seem to work. The only workable solution at this point is to redesign this part of the interface to make it simpler which is a shame. |
It's a surprisingly hard problem. Part of the issue is that even if you implemented it or worked around it, updating a dependent array (which seems to be be the most common use case) would take O(n*2) time, which is a big problem even for fairly small datasets. To get it to be O(n), we'd need smarter array primitives to compose dependent arrays. There has been *some work on this by Peter and others, and people are definitely interested in pushing it forward, but I wouldn't hold my breath. For now I'd recommend generating the dependent arrays on the server, basically as "computed relationships". |
Here's another fiddle demonstrating this issue: http://jsfiddle.net/3bGN4/268. |
Example workaround, demonstrating nested @each: |
If this is on the backburner, it would be helpful to note this behaviour in the API docs (maybe I missed it?) as it took me a while to find this ticket. |
@dandehavilland Exactly! It should definitly be noted here: 'http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/'. "@each.goo.bar" is still not working, i've needed days to find this out. |
Good idea. emberjs/website#653 |
@rwwagner90 the approach definitely works, I suspect there is another issue in your code causing this problem. Try posting a jsbin showing the problem to figure out what it is |
I might understand what you are getting at, could you modify my example to clarify though? |
@rwwagner90 regarding observers, my take on it is that using observers in cases when you're not interacting with non-ember code is a big code smell. So using observers to e.g. trigger a d3 graph rendering when your app data changes is totally legit, but using observers to set one ember property when another changes is almost always more easily accomplished with a different approach |
@alexspeller @kingpin2k I have two properties, and one must change when the other changes. This is not possible though because properties don't evaluate, so the second property never updates. |
@rwwagner90 having one property that changes when another changes is a core feature of computed properties, so I'm not sure what you're describing - if you show the code you're using it's likely that someone can help. |
@alexspeller I converted it all to using observes, but let me attempt to put it back and show you. let extendedObject = Ember.Object.extend({
valueLength: function() {
return this.get('value').length;
}.property('value.@each')
}); Then I have a property that should update when valueLength does: }.property('model.data.@each', '[email protected]'), However, this never fires, so I was forced to change my valueLength property to an observer that sets valueLength instead like so: let extendedObject = Ember.Object.extend({
valueLengthUpdater: function() {
this.set('valueLength', this.get('value').length);
}.observes('value.@each')
}); |
@rwwagner90 your dependency keys don't match what you are getting, your dependency key should be |
Also, Generally, observers are low level, and hard to get right, you shouldn't actually do work in them (it can lead to race conditions, overly frequent recalculations, etc) they should only schedule work once per event frame. |
Ditto @krisselden. Also, I'm assuming your model on some controller has a property Controller
|
Edit: You can replace Your dependent key then becomes:
See @krisselden's explanation below on why what I wrote previously won't work. @rwwagner90 valueLength is not necessary there - you can just use the value.length property. The property's dependent key can be simplified to:
|
@alexspeller Actually you can't |
@alexspeller get('@each.value') unfortunately returns an array of the values instead of returning an EachProxy which kills further chaining. I don't know why that was done that way but I don't know if I can change it given semver, and to be honest, I'm nervous about enabling this. |
The squeaky wheel gets the oil, until it's affecting a larger portion of the ember community it won't get prioritized up, there is plenty of other low hanging fruit that helps 90% of the community. |
@alexspeller Ember.computed.alias worked. Thanks! 👍 |
@krisselden Chaining |
I hope this issue is still on cause I don't know where to post this issue. I got the observer FIRED with this:
But with this, nothing happens
(The difference is the case of property name "a" and "A") So, anyone recognizes this issue, please help me explain. Thanks. |
Thanks. But I have it triggering with the lowercase "a".
And no computed property here. |
Oh, the upper case a is not supported. Use property names that start with lower case letters. |
Thanks,
I can't find documentation for that anywhere. |
Historically (pre-2.0), capitalized paths indicated a global (so in your case it would be observing Most (hopefully all) of the special casing of capitalized paths should be removed. We should test against 2.8.1. If it still isnt working on 2.8, please open a new issue with a twiddle/jsbin reproduction. |
This is extremely basically functionality which is sadly missing. It would be helpful to focus on getting the basics right before going off adding all these bells and whistles to the framework. |
@timothyerwin actually it's pretty easy to work around this. Also I think right now is a bad moment to change this. Things like '@Tracked' im glimmer show, that we need to re-think the entire CP-thing in the long term. Also don't get me wrong, I would love to have this sometimes! |
This is the Ember issue I encounter most frequently lately, often because it is hidden behind something else, like a filter or sort CP macro where it's easy to forget you effectively creating a dependent key that is an @each with nested properties. |
@luxferresum Would you mind posting a link to more information about how to work around this? Admittedly, I repeatedly get stumped by this, and I know I've been given some hints via community slack help channel, but I can't remember them nor can I seem to find a good post from searching the web. I think perhaps the trick was to break out each level as its own computed property somehow. So if I have an array like // Not only is this ugly, but it doesn't work since `_oneBigMess` won't get
// called again when something in one of its nested arrays changes,
// e.g. Changing traces[0].x[0] does not trigger 'traces.[]'
_oneBigMess: computed('traces.[]', function() {
return this.get('traces').reduce(function(result, item, i, a) {
return {
x: result.x.concat(item.x),
y: result.x.concat(item.y)
};
}, { x: [], y: []})
}),
_somethingChanged: observer('_oneBigMess.x.[]', '_oneBigMess.y.[]', function() {
// update chart
}) I'm sure the above code is idiotic to someone more experienced and familiar with the tools available, but I am grasping at straws here. but none seem to have a work-around for the situation I described 😢 Update: Someone please correct me if I'm wrong, but after fighting with this for a while tonight, it seems that the problem here isn't merely about providing more syntactic sugar but really about a limitation of the current system. If I understand correctly (based on the old comment above and some search results) this functionality isn't provided yet because there is not an efficient / clean way to do it. The advice that I was given was basically to not do this but rather change the design of my code so that I didn't need to. This is probably good advice but sometimes may not be feasible (e.g. insufficient time or requirements outside of one's control). For two-levels, one (pretty horrible & inefficient) work-around is to generate a long (maximum data size your code needs to handle correctly) list of keys, each associated with a single element of the array. For example: _watchChanges: observer('data.[]', 'data.@each.{name,type}',
...(new Array(100).fill(0).map((z,i) => `data.${i}.x.[]`)),
...(new Array(100).fill(0).map((z,i) => `data.${i}.y.[]`)), function() {
// something in one of these arrays has changed --> go do something
}) Another possibility (though I haven't tried it, and it's still not ideal) would be to iterate over the top-level array and use |
true. however there are much easer ways to handle this than you suggest. the primary trick/workaround here is to convert each trace into an ember object, and then have a CP on the Or with code:
actually it doesnt really matter what |
@luxferresum Thanks for taking the time to reply with a potential solution. However, it seems to have the same problem. Although creating a new For example, in the ember-cli-plotly addon I'm trying to make I have an integration test that changes the data to be plotted, and it works when I use a kludgy work-around where the main component generates a tagless child component for each trace, which then fire an action when data in their set changes. I will try your method a little more in case I'm just doing something wrong (very possible), but the way it's acting now makes me think that it's not as simple as you suggest. |
…rn/acorn-5.7.4 [Security] Bump acorn from 5.7.3 to 5.7.4
Adding an observer on a nested property of objects within a collection does not reliably fire. I've created a test case that could be added to
ember-runtime/tests/mixins/array_test.js
that illustrates this. Observing a nested property on the EachProxy itself appears to work fine. However, observing the property using@each.nest.isDone
does not.In the following test,
count1
is incremented to 1,count2
is not:The text was updated successfully, but these errors were encountered: