Following a great article by Ashley Grant (https://blog.ashleygrant.com/2017/07/09/getting-my-hands-dirty-with-aurelias-binding-engine/) I tried to accomplist task for dynamic binding to expressions coming from my backend server as json. This approach works great until I tried to apply value converters to this rebased binding chain. After that it fails with error “Cannot read property ‘valueConverters’ of undefined”.
My repro gist.run: https://gist.run/?id=6e8a1ef7825abbfe1ec93242d1adcbbc
Could anyone with deep aurelia binding knowledge help?
Sorry for confusion - there is local DebugValueConverter in app.js. Just noticed it has the same name as custom element - renamed it to LocalDebug for consistency
It’s not easy to re-base value converter, as you need to “re-sort” the value converter outside in, unlike binding behaviors, which don’t affect the evaluation. As the name suggested, you only re-base, not re-construct the expression, so I doubt it’s easy. What you can do is to walk through the expression and create a new expression instead of re-basing it.
1 Like
cc @AshleyGrant
I have just read Ashely’s post.
I can do it in simpler alternative way, without deep understanding of aurelia-binding.
My approach is to use value converter, not binding behavior. The result is human understandable code, plus more permissive in chain of value converters.
Note: beware the fromView
expects a simple propertyExpression like "address.city"
, otherwise the setExp
will fail. But this doesn’t mean GetValueConverter
cannot be used for complex expression like "address.state + ' ' + address.zip"
. You can still use GetValueConverter
in one-way binding with complex expression, where fromView
is never called.
Update: thx for @bigopon’s help, it now works correctly if used in more than 1 place.
@inject(Parser)
export class GetValueConverter {
constructor(parser) {
this.parser = parser;
}
toView(_: any, model: any, propertyExpression: string): any {
let getExp = this.parser.parse(propertyExpression);
return getExp.evaluate({ bindingContext: model });
}
fromView(value: any, model: any, propertyExpression: string): any {
let setExp = this.parser.parse(propertyExpression + ' = $value');
setExp.evaluate({
bindingContext: model,
overrideContext: { $value: value }
});
return model;
}
}
The downside is the binding is now verbose, you have to call model
twice in order to pass enough information to the value converter.
<input value.bind="model | get:model:property.expression | localDebug">
See it working here:
2 Likes
Thanks, works like a charm
Just found a problem with this solution - validation fails as validation lib is getting property by binding.sourceExpression
Seems like the ony way is to update sourceExpression somehow…
Gist here: https://gist.run/?id=6e8a1ef7825abbfe1ec93242d1adcbbc
I have no idea how to make aurelia-validation
to work with that. I am not an aurelia-validation
user.
But I have something you might be interested.
I have a different view on validation. My view is simple, validation is a function that takes rule and model as input, produces a structured errors object as output. I don’t want my view template doing anything with this kind of model layer logic. So I created something I consider simple enough and flexible enough.
Have a look of my implementation of your gistrun with my own tool bcx-validation
, the result is clean, flexible, and approachable code.
I updated my previous gistrun.
The current doc of bcx-validation
is very mathematical, without showing how to use it in Aurelia app. I need to write some nice examples of Aurelia usage in order to promote it to the community. The current doc is not ready for that now.