web-dev-qa-db-fra.com

angular possède-t-il la fonction "propriété calculée" comme dans vue.js?

J'ai d'abord appris Vue.js, et j'ai maintenant un projet dans Angular 4 donc je viens d'apprendre Angular. Je trouve que tout n'est pas si différent de Vue sauf le "Propriété calculée". Dans Vue, je peux créer une propriété calculée qui écoute les modifications d'autres propriétés et exécuter des calculs automatiquement.

Par exemple (dans Vue 2):

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

La propriété name ne sera recalculée que lorsque l'un des changements de prénom ou de nom de famille. Comment gérer cela dans Angular 2 ou 4?

22
Pano

oui, vous pouvez.

Dans le fichier TS:

export class MyComponent {

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

et ensuite en html:

<div>{{name}}</div>

voici un exemple:

@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
Julia Passynkova

Bien que cela soit déjà répondu, mais je pense que ce n'est pas une très bonne réponse et les utilisateurs ne devraient pas utiliser les getters comme propriétés calculées en angulaire. Pourquoi pouvez-vous demander? getter est juste une syntaxe sucrée pour la fonction et il sera compilé en fonction simple, cela signifie qu'il sera exécuté à chaque vérification de détection de changement. C'est terrible pour les performances car la propriété est recalculée des centaines de fois à chaque changement.

Jetez un œil à cet exemple: 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;
    }
}

Si vous voulez vraiment avoir des propriétés calculées, vous pouvez utiliser un conteneur d'état comme mobx

class TodoList {
    @observable todos = [];
    @computed get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length;
    }
}

mobx a @computed decorator donc la propriété getter sera mise en cache et recalculée uniquement si nécessaire

36
Andzej Maciusovic

Je vais essayer d'améliorer Andzej Maciusovic espère obtenir une réponse canonique. En effet VueJS a une fonctionnalité appelée propriété calculée qui peut être rapidement montrée à l'aide d'un exemple:

<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>

Chaque fois que l'utilisateur modifie la valeur d'entrée pour a ou b, product et productFunc se connectent à la console. Si l'utilisateur modifie c, seul productFunc est appelé.

Revenant à Angular, mobxjs aide vraiment avec ce problème:

  1. Installez-le à l'aide de npm install --save mobx-angular mobx
  2. Utilisez les attributs observable et computed pour les propriétés liées

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

Balisage

<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>

Si a ou b est modifié, AB est appelé une fois et getAB plusieurs fois. Si c est modifié, seul getAB est appelé. Ainsi, cette solution est plus efficace même lorsque le calcul doit être effectué .

13
Alexei

Dans certains cas, l'utilisation d'un Pure Pipe peut être une alternative raisonnable, cela vient évidemment avec certaines restrictions, mais cela évite au moins le coût de l'exécution de la fonction sur n'importe quel événement.

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

Dans votre modèle, au lieu d'une propriété de nom complet, vous pouvez peut-être simplement utiliser ' ' | join:firstname:lastname. Assez triste que les propriétés calculées n'existent toujours pas pour angulaire.

3
keyneom