не угловая есть "вычисляемое свойство" как в Vue.js?



Я выучил Vue.сначала js, а теперь у меня есть проект в Angular 4, поэтому я просто изучил Angular. Я нахожу, что все не так уж отличается от Vue, за исключением "вычисляемого свойства". В Vue я могу создать вычисляемое свойство, которое прослушивает изменения других свойств и автоматически выполняет вычисления.



Например (в Vue 2):



computed: {
name(){
return this.firstname + ' ' + this.lastname;
}
}


Свойство name будет пересчитываться только при изменении одного из значений firstname или lastname. Как справиться с этим в Angular 2 или 4 ?

534   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 действительно помогает в этом вопросе:

  1. установите его с помощью npm install --save mobx-angular mobx
  2. использование 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

    Ничего не найдено.