I’m posting this in case anyone else runs into this little snag.
While experimenting with aurelia-store, I decided I’d like to avoid making “temporary” properties on my view-model for the purpose of binding to form data. Instead, I wanted to bind my user input directly to the properties plucked from the state, and furthermore make them @observable, so I could easily dispatch a state update whenever any of them changed. (Is this a good idea? I don’t know; someone please weigh in if you have an opinion.)
import stuff
@autoinject
@connectTo({
selector: (store) => store.state.pipe(pluck("myProperty")),
target: "myProperty"
})
export class MyVM {
@observable myProperty; // OK - this will hold the latest plucked value, *and* be observable
myPropertyChanged(newVal, oldVal){
this.store.dispatch("updateMyProperty", newVal);
}
constructor(store){
store.registerAction("updateMyProperty", (state, newVal) => return Object.assign({},state, {myProperty: newVal});
}
}
<template>
<form><input type="text" value.bind="myProperty"></input></form>
</template>
Now assuming my syntax is correct (I haven’t tested the exact code above), this should pipe the value of store.state.myProperty into MyVM.myProperty; and any changes to MyVM.myProperty should dispatch an update to the store. And conveniently, @observable properties only call their change handler if newVal is different from oldVal, so we shouldn’t get stuck in an endless update loop that crashes our browser.
…right?
But that code will crash your browser!
Luckily, the reason and the solution are very simple: the @observable decorator and the @connectTo decorator both look for a change handler, and they both expect it to be named according to the same convention: [property-name]Changed(newVal, oldVal). Writing a single change handler that accidentally fits both those slots is very easy to do, and it causes some wild behaviour.
Solution: just use the syntax @observable({changeHandler: 'someOtherFunctionName'}) to define your observable change handler, and you’re all set. You can connectTo, pluck, observe/dispatch, and bind using a single property name, and it all works seamlessly.