Hey there, I’m a bit stuck in my brain to find a proper solution. In my application there is a model which got a list of child models. A getter isSelected returns a boolean if all children are selected. Dirty checking is not an option. How could I tell the binding engine to connect to the properties of all child objects?
class Parent {
children = [];
@computedFrom('children') // Will not trigger when only the property changed
get isSelected() {
if (!this.children.length) {
return false;
}
return this.children.every(child => child.selected);
}
}
My idea was to create a own computedFrom decorator which connects to changes of the items in the list and then connects to the children but so far it is tricky to understand the whole chain from parsing the binding interpolation to the observer on the getter.
If I have to watch a lot of properties on each list element I use the BindingEngine and dispose of all subscriptions in detached() and listChanged() and rebind them in attached() and listChanged() after the dispose.
For the update method I’ll just use a debounced function to wait until all list items have settled and 200ms later update anything dependent on selected.
import { BindingEngine } from 'aurelia-framework'; // Inject into view model
...
// Rebind Subs
this.subscriptions = [];
this.list.forEach(item => {
let sub = this.bindingEngine
.propertyObserver(item, 'selected')
.subscribe(() => this.delayedListUpdate());
this.subscriptions.push(sub);
});
...
// Dispose Subs
this.subscriptions.forEach(sub => sub.dispose());
this.subscriptions = [];
I wish there was a more intuitive way to do this, but I haven’t seen it yet.
Hey there, thanks for your suggestions but I don’t wanted to accept those not nice solutions and created my own decorator which allows me to tell that a getter is computed from list of objects.
class Child {
selected: boolean = false;
}
class Parent {
children: Child = [];
@computedFromList('children[*].selected') // Binds selected property of each object to this getter
get isSelected() {
return this.children.every(child => child.selected);
}
}
But I just figured out some limitations of Aurelia which requires the list of children not to be more than 100 for now.
@fragsalat that’s creative and glad to see you solved it. But I think we should emphasize @Sjaak 's solution, as it’s often the most efficient way to handle any case.
Definitely it would be the easier and probably better way to go but unfortunately our architecture doesn’t fit to that. The user can select 3 hierarchy levels like Grandparent, Parent, Child. Selecting Grandparents selects all Children of all Parents of those Grandparents. On the other wside when all children of a parent or all parents of a grandparent are selected, the grandparent is considered as selected as well. What I want to say with it is that the selection is not called on the parent or grandparent but always on children for having a generic selection logic which makes this construct not work. The children also doesn’t and shouldn’t have a relation to it’s parent.
I was only saying that to encourage folks to try @Sjaak solution before using yours. It seems to be suitable for most cases. But there are situations where yours makes more sense. I didn’t mean yours is not good.
Paths are definitely supported, we have countless cases in our application.
This should work: @computedFrom("profileNameSetting", "profileNameSetting.value")
it’s enough to pass “profileNameSetting.value” to the computedFrom. you don’t need to specify “profileNameSetting”.
but you do need to acount for null inside your getter. return this.profileNameSetting.value; will throw when this.profileNameSetting is null or undefined.
simply change your code to
@computedFrom("profileNameSetting.value")
get profileName(){
return this.profileNameSetting && this.profileNameSetting.value;
}
// observe for changes at property profileNameSetting,
// and also observe the properties inside the value of profileNameSetting
@deepComputedFrom('profileNameSetting')
It needs to be known upfront. But you can add via replacing the entire nested object. Im notnsure how to overcome this limitation for now. Same applies for deleting properties.
I started to build something like an observable object for aurelia using es6 proxy some time back. theres a few issues with it (array prototype methods for instance like splice, updating the reference tree) that I haven’t had a chance to revisit. Here it is if you have any ideas on how to improve it: https://github.com/jbockle/aurelia-stores
it watches deeply, means it will observe everything in the root object, and any object nested within it. Array is object too so yes. For array, it observes differently though, same applies for Map/Set