@bigopon Sorry for pinging you. Did you have a chance to look for something in Aurelia that would help me to deep observe such “complex” structure? I will have quite a lot of forms similar to this, and I would like to provide auto-save functionality.
Obviously, I can autosave on every “addSomething” and “removeSomething”, but I would also need to set up/release propertyObservers in these methods to observe text fields, selects etc. in the row. Sounds quite like a lot of messy code, which could/should be avoided.
Or is there any other recommended approach to achieve what I need?
If the purpose is to get notified whenever there’s a modification deepdown, then maybe you don’t need to worry about setup/release the observer? You only need to recalculate the json data value
@bigopon Sorry, I don’t understand your point. I only need to recalculate the JSON data value. That’s correct. The problem is I don’t know when to do so, which is why I wanted to use observer.
Very ugly workaround would be to calculate JSON every few milliseconds, compare it to previous value, and save on change. But I hoped to avoid that.
class App {
data = { ... }
@deepComputedFrom('data')
get jsonData() {
return JSON.stringify(this.data);
}
}
You can see that it: tracks changes deep within the object, and doesn’t do dirty checking. Pros & cons:
Pros:
simple change tracking
Cons:
if the object is big (contains array with many objects and a lot of properties), it’s costly upfront setup. (Though it needs to be quite a big object to be perceivable here)
It’s just a POC, need to properly release the observer and re-observe when array/object is mutated
Edit:
sorry for pinging …
No problems at all, i should say sorry that i missed your reply.
There’s also another way to solve it: declare a counter property, an on mutation, do an increment. If it’s a two way binding with an input field, then you can listen to input event and do the increment. That will be the simplest of all.
I may make the above @deepComputedFrom into a plugin, but no promise
Sorry for being so late (life’s got in the way), but I would like to say big THANK YOU for this. To get rid of a bit of my shame and show some appreciation, I’ve at least doubled my mothly OpenCollective contributions .
That said, similar to what @jeremyholt asked, what would be your recommended approach to observe changes using this plugin? I don’t think simple @observable would help here - if I understand it correctly, it cannot be used on computed properties and it is not “deep”.
Let’s say I have following in my VM:
class App {
originalModel: ComplexModel;
formModel: ComplexModel;
activate() {
this.originalModel = await loadFromApi();
this.formModel = deepClone(this.originalModel);
}
@computedFrom('originalModel')
@deepComputedFrom('formModel')
get isDirtyComputed() {
return deepEquals(this.originalModel, this.formModel);
}
// Q: How to call this method on change of isDirtyComputed?
save() {
await saveToApi(this.formModel);
this.originalModel = this.formModel;
}
}
This is probably more of a general question of how to observe changes in computed properties, but anyway. My hacky approach based on my limited Aurelia knowledge would be to:
bind something to isDirtyComputed from the View
create @observable isDirty: boolean on the VM
change isDirtyComputed implementation to return this.isDirty = deepEquals(this.originalModel, this.formModel);
implement isDirtyChanged(newValue) { if (newValue) this.save(); }
But that is not very nice, and most importantly would not work if isDirtyComputed in not used on the View (correct me if I am wrong). Is there a cleaner way to do that?
Thank you very much @bigopon .
This does what the docs say about expressionObserver (“notify you when any property within the path of expressionString has been changed”).
I´m not a native speaker tough, maybe i got the docs wrong.
I’ve only used PropertyObserver and ExpressionObserver before. When talking about my usecase, ObserverLocator is some smart thing above these, returning the right observer for the target, supporting @computedFrom (and @deepComputedFrom) properties in addition to class fields, arrays, and expressions?
One last question: can I use both @deepComputedFrom and @computedFrom on a single property at the same time, to save few ticks on properties I know will never change “inside” (originalModel in my case)?
Once again, thank you for all your explanations and the plugin.
Arr please don’t say that, it’s just the inner stuff that BindingEngine uses. You just reminded me that you can also use PropertyObserver too, so bindingEngine.propertyObserver(obj, 'isDirty').subscribe is the one.
One last question: can I use both @deepComputedFrom and @computedFrom on a single property at the same time, to save few ticks on properties I know will never change “inside” (originalModel in my case)?
@computedFrom will give hint to observer locator about how to get observer, so it will never reach @deepComputedFrom, so you can’t use them both together.
Hm, I didn’t realize it is possible to use propertyObserver on computed props . Is it also possible to use @observable decoractor on them, or they work differently?
I’ve just learned that it is possible to use propertyObserver (or observerLocator) on a computed get x() property. So I wouldn’t be surprised if @observable decorator is usable on such properties too, internally using the same logic / components.
@bigopon thanks, I use the plugin in my current project and it works great. One note: in the npm package description, I believe you did a mistake in the second example of “Rendered text will be:”