У angular есть функция "вычисленного свойства", например, в vue.js?
Сначала я узнал Vue.js, и теперь у меня есть проект в Angular 4, поэтому я только что узнал Angular. Я считаю, что все не отличается от Vue, кроме "Вычисленного свойства". В Vue я могу создать вычисляемое свойство, которое прослушивает изменения других свойств и автоматически запускает вычисления.
Например (в Vue 2):
computed: {
name(){
return this.firstname + ' ' + this.lastname;
}
}
Свойство name будет только пересчитываться при изменении одного из имени или фамилии. Как справиться с этим в Angular 2 или 4?
Ответы
Ответ 1
да, вы можете.
В файле TS:
export class MyComponent {
get name() {
return this.firstname + ' ' + this.lastname;
}
}
а затем в html:
<div>{{name}}</div>
вот пример:
@Component({
selector: 'my-app',
template: `{{name}}`,
})
export class App {
i = 0;
firstN;
secondN;
constructor() {
setInterval(()=> {
this.firstN = this.i++;
this.secondN = this.i++;
}, 2000);
}
get name() {
return this.firstN + ' ' + this.secondN;
}
}
Ответ 2
Хотя на этот вопрос уже дан ответ, но я думаю, что это не очень хороший ответ, и пользователям не следует использовать геттеры в качестве вычисляемых свойств в angular. Почему вы можете спросить? getter - это просто сахарный синтаксис для функции, и он будет скомпилирован в обычную функцию, это означает, что он будет выполняться при каждой проверке обнаружения изменений. Это ужасно для производительности, потому что свойство пересчитывается сотни раз при любом изменении.
Посмотрите на этот пример: https://plnkr.co/edit/TQMQFb?p=preview
@Component({
selector: 'cities-page',
template: '
<label>Angular computed properties are bad</label>
<ng-select [items]="cities"
bindLabel="name"
bindValue="id"
placeholder="Select city"
[(ngModel)]="selectedCityId">
</ng-select>
<p *ngIf="hasSelectedCity">
Selected city ID: {{selectedCityId}}
</p>
<p><b>hasSelectedCity</b> is recomputed <b [ngStyle]="{'font-size': calls + 'px'}">{{calls}}</b> times</p>
'
})
export class CitiesPageComponent {
cities: NgOption[] = [
{id: 1, name: 'Vilnius'},
{id: 2, name: 'Kaunas'},
{id: 3, name: 'Pabradė'}
];
selectedCityId: any;
calls = 0;
get hasSelectedCity() {
console.log('hasSelectedCity is called', this.calls);
this.calls++;
return !!this.selectedCityId;
}
}
Если вы действительно хотите иметь вычисляемые свойства, вы можете использовать контейнер состояний, такой как mobx
class TodoList {
@observable todos = [];
@computed get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length;
}
}
mobx имеет @computed decorator, поэтому свойство getter будет кэшироваться и пересчитываться только при необходимости
Ответ 3
Я постараюсь улучшить Andzej Maciusovic
в надежде получить канонический ответ. Действительно, в VueJS есть функция, называемая вычисляемым свойством, которую можно быстро показать на примере:
<template>
<div>
<p>A = <input type="number" v-model="a"/></p>
<p>B = <input type="number" v-model="b"/></p>
<p>C = <input type="number" v-model="c"/></p>
<p>Computed property result: {{ product }}</p>
<p>Function result: {{ productFunc() }}</p>
</div>
</template>
<script>
export default {
data () {
return {
a: 2,
b: 3,
c: 4
}
},
computed: {
product: function() {
console.log("Product called!");
return this.a * this.b;
}
},
methods: {
productFunc: function() {
console.log("ProductFunc called!");
return this.a * this.b;
}
}
}
</script>
Всякий раз, когда пользователь изменяет входное значение для a
или b
, оба product
и productFunc
регистрируются на консоли. Если пользователь изменяет c
, вызывается только productFunc
.
Возвращаясь к Angular, mobxjs действительно помогает с этой проблемой:
- Установите его, используя
npm install --save mobx-angular mobx
- Используйте атрибуты
observable
и computed
для связанных свойств
Файл TS
import { observable, computed } from 'mobx-angular';
@Component({
selector: 'home',
templateUrl: './home.component.html',
animations: [slideInDownAnimation]
})
export class HomeComponent extends GenericAnimationContainer {
@observable a: number = 2;
@observable b: number = 3;
@observable c: number = 4;
getAB = () => {
console.log("getAB called");
return this.a * this.b;
}
@computed get AB() {
console.log("AB called");
return this.a * this.b;
}
}
Markup
<div *mobxAutorun>
<p>A = <input type="number" [(ngModel)]="a" /> </p>
<p>B = <input type="number" [(ngModel)]="b" /> </p>
<p>C = <input type="number" [(ngModel)]="c" /> </p>
<p> A * B = {{ getAB() }}</p>
<p> A * B (get) = {{ AB }}</p>
</div>
Если a
или b
изменены, AB
вызывается один раз, а getAB
- несколько раз. Если c
изменяется, вызывается только getAB
. Таким образом, это решение более эффективно, даже если необходимо выполнить вычисления.
Ответ 4
В некоторых случаях использование Pure Pipe может быть разумной альтернативой, очевидно, что это связано с некоторыми ограничениями, но, по крайней мере, позволяет избежать дороговизны выполнения функции в любом случае.
@Pipe({ name: 'join' })
export class JoinPipe implements PipeTransform {
transform(separator: string, ...strings: string[]) {
return strings.join(separator);
}
}
В вашем шаблоне вместо свойства полного имени вы можете вместо этого просто использовать ' ' | join:firstname:lastname
. Довольно грустно, что вычисляемые свойства по-прежнему не существуют для angular.
Ответ 5
Свойства computed
в Vue имеют 2 огромных преимущества: реактивность и запоминание.
В Vue я могу создать вычисляемое свойство, которое прослушивает изменения других свойств и автоматически выполняет вычисления.
Вы спрашиваете здесь конкретно о системе реактивности в Angular. Кажется, что люди забыли, что является краеangularьным камнем Angular: Observables.
const {
BehaviorSubject,
operators: {
withLatestFrom,
map
}
} = rxjs;
const firstName$ = new BehaviorSubject('Larry');
const lastName$ = new BehaviorSubject('Wachowski');
const fullName$ = firstName$.pipe(
withLatestFrom(lastName$),
map(([firstName, lastName]) => 'Fullname: ${firstName} ${lastName}')
);
const subscription = fullName$.subscribe((fullName) => console.log(fullName))
setTimeout(() => {
firstName$.next('Lana');
subscription.unsubscribe();
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.js"></script>