I am currently working on porting some code that I have written from Knockout to Angular2. I really like the construct of computed observables in knockout which means the results of that function are only computed when the obeservables it depends on change.
I know I can use a function in angular and it will only update the view when the results change (even though it will calculate the results many times), but my computed is doing sorting, which is why I only want the work to be done when the inputs actually change.
I've found the links below that explain how to do it with angularjs, but I'm not sure how to translate that to angular2 (typescript)
http://www.jomendez.com/2015/02/06/knockoutjs-computed-equivalent-angularjs/ KO.Computed equivalent in Angular / Breeze Initializer
I would consider a getter if you are using TypeScript. You could also make your Component use ChangeDetectionStrategy.OnPush to make sure the binding is only evaluated if one of the properties changed. Here is a Plunker: https://plnkr.co/edit/PjcgmFCDj8UvBR7wRJs2?p=preview
import {Component,ChangeDetectionStrategy} from 'angular2/core'
@Component({
selector:'person',
template:`<div>
<div>
First Name
<input [(ngModel)]="firstName">
</div>
<div>
Last Name
<input [(ngModel)]="lastName">
</div>
{{fullName}}
</div>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Person{
firstName = '';
lastName = '';
get fullName(){
return this.firstName + ' ' + this.lastName;
}
}
The closest analogue of ko.observable
is Subject
or even BehaviorSubject
, and for ko.computed
you may use Observable.combineLatest
Here kind of hello world example:
import 'rxjs/add/operator/map';
import {Component} from '@angular/core';
import {Observable, BehaviorSubject} from "rxjs";
@Component({
selector: 'app-root',
template: `<div>
<button (click)="both()">both</button>
<button (click)="first()">first</button>
<button (click)="last()">last</button>
<button (click)="ageNow()">age</button>
<hr />
fullName: {{fullName | async}}
<hr />
age: {{age | async}}
</div>`
})
export class AppComponent {
firstName = new BehaviorSubject(''); // aka this.firstName = ko.observable('')
lastName = new BehaviorSubject('');
age = new BehaviorSubject(0);
fullName = Observable.combineLatest(this.firstName, this.lastName) // aka this.fullName = ko.computed(...)
.do(values => console.log('computed fired'))
.map(values => values.join(' ').trim());
both() {
this.first();
this.last();
}
first() {
this.firstName.next('foo ' + Date.now());
}
last() {
this.lastName.next('bar ' + Date.now());
}
ageNow() {
this.age.next(Date.now());
}
}
And probably you will want to get it working with forms then example will be something like this one:
import 'rxjs/add/operator/map';
import {Component} from '@angular/core';
import {Observable, BehaviorSubject} from "rxjs";
import {FormGroup, FormControl, FormBuilder} from "@angular/forms";
@Component({
selector: 'app-root',
template: `<form [formGroup]="form">
<input formControlName="firstName" />
<input formControlName="lastName" />
{{fullName | async}}
</form>`
})
export class AppComponent {
form:FormGroup;
firstName = new FormControl('');
lastName = new FormControl('');
fullName = Observable.combineLatest(
this.firstName.valueChanges.startWith(''),
this.lastName.valueChanges.startWith('')
).map(values => values.join(' ').trim());
constructor(private fb:FormBuilder) {
this.form = fb.group({
firstName: this.firstName,
lastName: this.lastName
});
}
}
Note that in form example we are watching not for FormControl but for its build in valueChanges
stream, also we define initial value for it.
If you wish not to deal with | async
pipes in templates you always can subscribe to your streams and them component properties
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With