не угловая есть "вычисляемое свойство" как в Vue.js?
Я выучил Vue.сначала js, а теперь у меня есть проект в Angular 4, поэтому я просто изучил Angular. Я нахожу, что все не так уж отличается от Vue, за исключением "вычисляемого свойства". В Vue я могу создать вычисляемое свойство, которое прослушивает изменения других свойств и автоматически выполняет вычисления.
Например (в Vue 2):
computed: {
name(){
return this.firstname + ' ' + this.lastname;
}
}
Свойство name будет пересчитываться только при изменении одного из значений firstname или lastname. Как справиться с этим в Angular 2 или 4 ?
4 ответов:
Да, можешь.
В файле 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; } }
Хотя это уже ответ, но я думаю, что это не очень хороший ответ, и пользователи не должны использовать геттеры в качестве вычисляемых свойств в angular. Почему вы можете спросить? геттер-это просто синтаксис сахара для функции, и он будет скомпилирован в простую функцию, это означает, что он будет выполняться при каждой проверке обнаружения изменений. Это ужасно для производительности, потому что свойство пересчитывается сто раз при любом изменении.
Взгляните на этот пример.: 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 будет кэшироваться и пересчитываться только при необходимости
Я постараюсь улучшить
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 file
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; } }Разметка
<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. Таким образом, это решение более эффективно даже тогда, когда вычисления должны выполняться.
В некоторых случаях использование чистого канала может быть разумной альтернативой, очевидно, что это связано с некоторыми ограничениями, но это, по крайней мере, позволяет избежать дорогостоящего выполнения функции на любом событии.
@Pipe({ name: 'join' }) export class JoinPipe implements PipeTransform { transform(separator: string, ...strings: string[]) { return strings.join(separator); } }В вашем шаблоне вместо свойства полного имени вы можете просто использовать
' ' | join:firstname:lastname. Довольно грустно, что вычисляемые свойства все еще не существуют для angular.
Comments