Reputation: 1640
I'm using Aurelia with TypeScript. I'm trying to bind an object to the view and I want the view to update when I change a property of my object. I know that Aurelia only watches the object and some array manipulations (push, splice etc). I also know that there are some binding helpers such as @computedFrom and using the BindingEngine but I still can't find the best approach when using value converters.
In my example I have class in TypeScript, e.g. "class Car". Then I bind multiple car objects to the view, e.g. ${car1}, ${car2} etc. I add a value converter to present the car, e.g. ${car1 | carPresenter}. This displays the information like this: "A blue car with full tank and three passengers". If I change a car property, e.g. "car1.passengers++" then I want the ${car1 | carPresenter} to update.
Maybe a value converter is the wrong approach? Please advise on better methods if that's the case. I want to present complex objects by showing some of it's properties but not necessarily all of them. And those presentations should update when the underlying properties change.
I have created a gist with a simplified example that illustrates the problem: https://gist.run/?id=06287de5eb179b32fa0b134eb59ebc68
Upvotes: 3
Views: 3634
Reputation: 2134
If you want to bind and react to inner changes, here's a little trick of mine...
get observe()
getter to the class that return { self: this };
car.observe.self
So in your case the Car class should have a getter that looks like this:
@computedFrom('color', 'gasLevel', 'passengers')
get observe() {
return { self: this };
}
And your html binding would look like this:
<strong>car: </strong>${car.observe.self | carPresenter}
Quick, simple, readable, and gets the job done (and supports inner observation nestling).
app.js
import { Car } from './car';
export class App {
car = new Car({
color: 'blue',
gasLevel: 'full',
passengers: 1
});
attached() {
this.increasePassengers();
}
increasePassengers() {
this.car.passengers++;
setTimeout(() => { this.increasePassengers(); }, 1000);
}
}
car.js
import { computedFrom } from 'aurelia-framework';
export class Car {
color = undefined;
gasLevel = undefined;
passengers = undefined;
constructor(data) {
const values = data || {};
const fieldNames = Object.getOwnPropertyNames(values);
for (let field of fieldNames) {
if (field in this) {
this[field] = values[field];
}
}
}
@computedFrom('color', 'gasLevel', 'passengers')
get observe() {
return { self: this };
}
}
Q: Why not simply get self(): { return this; }
A: Aurelia is too smart for that; it sees that the same reference was returned and ignores it.
Upvotes: 0
Reputation: 1257
There is an additional binding decorator you can leverage: @observable
[related docs].
More info: Working With Aurelia @observable (Dwayne's blog is an extremely useful resource for learning Aurelia).
Gist demo: https://gist.run/?id=c359860951717457e630e3fde1a4d6aa
In this demo, the Car
object has its own class defined, where all necessary properties have an @observable
decorator. Value converter has been replaced by description
getter method.
Car class
import { observable } from 'aurelia-framework';
export class Car {
@observable
color;
@observable
gasLevel;
@observable
passengers;
constructor(data) {
// ...
}
get description() {
return `A ${this.color} car with ${this.gasLevel} tank and ${this.passengers} passengers`;
}
}
Upvotes: 3