-
Notifications
You must be signed in to change notification settings - Fork 27.4k
angular.copy and angular.equals don't play well with MobX #15533
Comments
This has nothing to do with MobX (in the sense that it is in no-way MobX-specific). So, the title is unnecessarily specific imo. This is basically a general feature request about supporting That said, there have been some concerns about allowing developers to tweak methods that are so fundamental for the entire framework - essentially making it easier for people to shoot themselves in the foot. TBH, I myself am not very keen on this feature. |
If we keep this issue MobX-specific, we might consider also other ways of solving it. E.g. finding why these functions don't work with observable arrays and fixing the reason. I guess it's #5085. Also it'd be interesting to discuss a deeper integration of MobX with watchers. |
Dealing with the unpatchable equals/copy of $watch has become a lot more of a problem in ES6 I think. There’s no such thing as a generic ‘equals’ or ‘clone’ method in JS (not sure if there ever was, really), and the current implementation makes some curiously unsafe assumptions. For example, it doesn’t copy accessors (reasonable default) on the target object, but because it does copy the prototype chain, it effectively copies only accessors which are not own-properties, a very strange result! Commonly, accessors involve some form a private state at the instance level, e.g. symbol keyed shadow properties or a class-wide WeakMap registry keyed on instances. Both of these will break in the clone. One could say, ‘well, you can’t watch an object like that’ ... except I think such objects are more and more common, and few devs actually think about how Angular’s watches work. While I don’t fully understand all the reservations expressed in the #10096 thread, I’m wondering if a solution of a simple contract would please everybody — e.g. The advantages, I think, would be:
I feel like this idea is either really awesome or really dumb, but it’s 2:32 AM here and I could go either way. Edit: Just saw that this same solution was proposed in #5085, but without the only-primitive-value constraint Edit edit: Whoa ... it already exists ... though it’s a hack. |
@thorn0, the reason this "doesn't work" with MobX is that We might be able to make MobX ObservableArray work, by relaxing function isArray(arr) {
return Array.isArray(arr) || (arr instanceof Array);
} ...which is a breaking change, but a reasonable one imo. But other MobX stuff or other implementations will certainly still not work. If we want to solve this, then we better do it in a more generic way (instead of having to solve it for each "MobX" every now and then). @bathos, what you propose is a viable approach (and has been discussed before as you found out). The problem is that everything in BTW, taking advantage of |
👍 I've tested it and at least in my project, this seems to be enough for all the watchers to start working fine with MobX observables. I'll submit a PR. |
The But it was only fixed locally in that one usage. Switching to |
In particular, this change makes Angular play well with MobX observable arrays. See angular#15533
In particular, this change gets Angular to play nicely with MobX observable arrays. See angular#15533 BREAKING CHANGE: Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`. If you need this logic, use `Array.isArray` directly.
BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. This change also affects `angular.merge`, `angular.forEach`, and `angular.equals`. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. This change also affects `angular.merge`, `angular.forEach`, and `angular.equals`. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Some objects that weren't previously recognized as arrays (e.g. MobX observable arrays, see angular#15533) by the watching logic are now recognized as such. This change also affects `angular.merge`, `angular.forEach`, and `angular.equals`. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Closes angular#15533 BREAKING CHANGE: `angular.isArray` is used by `angular.copy`, which in turn is used internally by the dirty checking logic. That's why this change affects the way objects are copied and watched by AngularJS. Objects that prototypally inherit from `Array` (e.g. MobX observable arrays, see angular#15533) weren't previously recognized as arrays, now they are. This change also affects `angular.merge`, `angular.forEach`, and `angular.equals`. Previously, `angular.isArray` was an alias for and thus worked exactly as `Array.isArray`.
Do you want to request a feature or report a bug?
feature
What is the current behavior?
In theory, it should be easy to start working with MobX. It will be fun they said. Just replace plain objects and arrays with observables and the existing code shouldn't notice much difference. Just a couple of tweaks might be needed. Unfortunately, it's not the case with Angular 1.x apps. Watchers get broken when they are passed observable arrays.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).
$watch
ignores changes in observable arrays:http://plnkr.co/edit/eNBaj5HBvvGdd6PAos8g?p=preview
$watch
usesangular.copy
andangular.equals
internally, and it's these functions that don't work with MobX:What is the expected behavior?
Would be great to have a way to teach
angular.copy
andangular.equals
new object types.What is the motivation / use case for changing the behavior?
I decided to play with MobX in my app to see whether it can make the state management simpler. As the first step, I was going to just replace my model/state objects with observables to see what changes are needed.
Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
Angular 1.6, 1.5
Other information (e.g. stacktraces, related issues, suggestions how to fix)
This issue may sound like #10096, but it's not about specifying an alternative
copy
andequals
implementation on every call to$watch
. A way to override this logic globally for all the watchers, directive bindings, etc. is needed.The text was updated successfully, but these errors were encountered: