Source: https://github.com/enepomnyaschih/mt/tree/mt-2.1-5 (Git branch).
Now it's the time to learn a new layer of jWidget: Property and its interfaces Bindable and IProperty. Property is a value that can notify the clients about its modification. So, Property is a class with two methods: get, set - and an event changeEvent. Whenever you change the value of the property with set method call, changeEvent is triggered. The event is not triggered if the value is not changed in result of set method call.
So, in our Mini-Twitter example, we can simplify Like/Unlike and Retweet/Unretweet behaviour implementation by introducing two boolean properties.
First, let's change the model. We need to replace simple boolean fields with properties.
import IProperty from "jwidget/IProperty"; import Property from "jwidget/Property"; export default class Tweet { // ... readonly like: IProperty<boolean>; readonly retweet: IProperty<boolean>; constructor(config: TweetConfig) { this.fullName = config.fullName; this.shortName = config.shortName; this.avatarUrl48 = config.avatarUrl48; this.contentHtml = config.contentHtml; this.time = config.time; this.like = new Property(config.like); this.retweet = new Property(config.retweet); }
We can now remove the corresponding events and setters - it is quite a chunk of code. Let's modify the view now. Instead of listening property change event manually, we can utilize property bindings.
First, we're going to build string properties with "Like/Unlike" and "Retweet/Unretweet" values from original boolean properties. To do so, we can use map method. Since life time of the model can be longer than life time of the view (because the model can live without a view, and not vice versa), it is important to aggregate this binding in the view with own method.
Second, we're going to bind DOM elements to all these properties with bindText function. It doesn't make much sense to aggregate this binding, because the source property belongs to the view itself, and the binding is so simple that it doesn't create any objects in the output.
Third, we should also bind "active" CSS class presence to the values of corresponding properties with bindClass function. Since this is a binding to model, we must aggregate it with own method.
protected renderLike(el: JQuery) { const text = this.own(this.tweet.like.map(like => like ? "Unlike" : "Like")); bindText(el, text); this.own(bindClass(el, "active", this.tweet.like)); el.on("click", event => { event.preventDefault(); this.tweet.like.set(!this.tweet.like.get()); }); } protected renderRetweet(el: JQuery) { const text = this.own(this.tweet.retweet.map(retweet => retweet ? "Unretweet" : "Retweet")); bindText(el, text); this.own(bindClass(el, "active", this.tweet.retweet)); el.on("click", event => { event.preventDefault(); this.tweet.retweet.set(!this.tweet.retweet.get()); }); }
And we can delete methods _updateLike and _updateRetweet - we don't use them anymore.
Run the application.
The syntax of properties and bindings is clean and straightforward. They let you make code shorter and more readable. Take a look at Bindable documentation for full list of bindings.
Tutorial. Part 6. Collection bindings