web-dev-qa-db-ja.com

angularにはvue.jsのような「計算プロパティ」機能がありますか?

私は最初にVue.jsを学び、今ではAngular 4にプロジェクトがあるので、Angularを学びました。すべてがVue 「計算プロパティ」:Vueでは、他のプロパティの変更をリッスンし、計算を自動的に実行する計算プロパティを作成できます。

例えば(in Vue 2):

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

Nameプロパティは、firstnameまたはlastnameのいずれかが変更された場合にのみ再計算されます。これをAngular 2または4で処理するには?

22
Pano

はい、できます。

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;
  }
}
12

これはすでに答えられていますが、これはあまり良い答えではなく、ユーザーは角度で計算されたプロパティとしてゲッターを使用すべきではないと思います。なぜ尋ねるか? 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デコレータがあるため、getterプロパティはキャッシュされ、必要な場合にのみ再計算されます

36

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の入力値を変更するたびに、productproductFuncの両方がコンソールにログを記録します。ユーザーがcを変更すると、productFuncのみが呼び出されます。

Angularに戻ると、 mobxjs はこの問題に本当に役立ちます。

  1. npm install --save mobx-angular mobxを使用してインストールします
  2. バインドされたプロパティには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;
       }
    }

マークアップ

<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が1回呼び出され、getABが数回呼び出されます。 cが変更された場合、getABのみが呼び出されます。したがって、このソリューションは、計算を実行する必要がある場合でもより効率的です

13
Alexei

場合によっては、Pure Pipeを使用するのが合理的な代替策である可能性があります。明らかに、いくつかの制限がありますが、少なくともイベントで関数を実行するコストを回避します。

@Pipe({ name: 'join' })
export class JoinPipe implements PipeTransform {
  transform(separator: string, ...strings: string[]) {
    return strings.join(separator);
  }
}

テンプレートでは、フルネームプロパティの代わりに、' ' | join:firstname:lastnameを使用することもできます。計算されたプロパティがまだ角度に対して存在しないことはかなり悲しいです。

3
keyneom