angular有像vue.js那样的“计算属性”特性吗?

2jcobegt  于 2023-06-24  发布在  Vue.js
关注(0)|答案(9)|浏览(268)

我首先学习了Vue.js,现在有一个Angular 4的项目,所以我刚刚学习了Angular。我发现除了“计算属性”之外,一切都与Vue没有什么不同。在Vue中,我可以创建一个计算属性,它可以监听其他属性的变化,并自动运行计算。
例如(在Vue 2中):

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

name属性仅在firstname或lastname之一更改时才重新计算。如何在Angular 2或4中处理这个问题?

fzsnzjdm

fzsnzjdm1#

虽然这已经得到了回答,但我认为这不是一个很好的答案,用户不应该使用getter作为angular中的计算属性。为什么你会问?getter只是函数的sugar语法,它将被编译为普通函数,这意味着它将在每次更改检测检查时执行。这对性能来说是很糟糕的,因为属性在任何更改时都要重新计算数百次。
看看这个例子: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属性将被缓存,并仅在需要时重新计算

ruoxqz4g

ruoxqz4g2#

我将尝试改进Andzej Maciusovic的,希望获得一个规范的答案。事实上,VueJS有一个名为computed property的特性,可以通过一个例子快速展示:

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

每当用户更改ab的输入值时,productproductFunc都会登录到控制台。如果用户更改c,则仅调用productFunc
回到Angular,mobxjs确实有助于解决这个问题:
1.使用npm install --save mobx-angular mobx安装
1.将observablecomputed属性用于绑定属性

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>

如果ab被更改,则AB被调用一次,getAB被调用多次。如果c被更改,则只调用getAB。因此,即使必须执行计算,此解决方案也更有效

ltqd579y

ltqd579y3#

Vue中的computed属性有两个巨大的好处:React性和记忆性。
在Vue中,我可以创建一个计算属性,它可以监听其他属性的变化,并自动运行计算。
你在这里特别询问Angular中的React性系统。似乎人们已经忘记了什么是Angular基石:可观测的。

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>

我的代码没有解决记忆部分,它只处理React性。
人们给出了一些有趣的答案。虽然mobX computed属性可能(我从未使用过mobX)更接近Vue中的computed属性,但这也意味着您依赖于另一个可能不感兴趣的库。对于您所解释的特定用例,管道替代方案是最有趣的,因为它同时解决了React性和记忆化问题,但它并不适用于所有情况。

daolsyd0

daolsyd04#

在某些情况下,使用纯管道可能是一个合理的替代方案,显然这会带来一些限制,但它至少避免了在任何事件上执行函数的代价。

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

在你的模板中,你可以使用' ' | join:firstname:lastname来代替全名属性。很遗憾,angular的计算属性仍然不存在。

pkmbmrz7

pkmbmrz75#

可以。
在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;
  }
}
ndh0cuux

ndh0cuux6#

你可以从Vue composition API中导入两个函数,它工作得很好。这可能是个坏主意,但很有趣。只需从Vue导入refcomputed,就可以在Angular中拥有计算属性。
我有一个例子PR,我将Vue添加到Angular项目中:https://github.com/kevin-castify/vue-in-angular/pull/1/files
1.将Vue添加到您的项目中,至少使用版本3
1.添加到您的组件。ts像这样:

import { Component, OnInit } from '@angular/core';
import { ref, watch } from 'vue';

...

export class FullNameComponent implements OnInit {
  firstName = ref('');
  lastName = ref('');
  fullName = computed(() => this.firstName.value + this.lastName.value;

  ngOnInit(): void {
    // Might need to seed the refs here to trigger the first computation
    firstName.value = "Jane";
    lastName.value = "Doe";
  }
}
bvjxkvbb

bvjxkvbb7#

我想再添加一个选项(TypeScript 4),因为上面提到的方法并不能100%满足所有需求。它不是React性的,但仍然足够好。其思想是显式声明一个检测更改的函数和一个计算属性值的函数。

export class ComputedProperty<TInputs extends any[], TResult> {
    private readonly _changes: (previous: TInputs) => TInputs;
    private readonly _result: (current: TInputs) => TResult;
    private _cache: TResult;
    private _inputs: TInputs;

    constructor(changes: (previous: TInputs) => TInputs, result: (current: TInputs) => TResult) {
        this._changes = changes;
        this._result = result;
    }

    public get value(): TResult {
        const inputs = this._changes(this._inputs);
        if (inputs !== this._inputs) {
            this._inputs = inputs;
            this._cache = this._result(this._inputs);
        }
        return this._cache;
    }
}

兹宣布:

// readonly property
this.computed = new ComputedProperty<[number], number>(
        (previous) => {
            return previous?.[0] === this.otherNumber ? previous : [this.otherNumber];
        },
        (current) => {
            return current[0] + 1;
        }
    );

用途:

<label>Angular computed property: {{computed.value}}</label>
mdfafbf1

mdfafbf18#

更新(2023年5月)

Angular在Angular 16 https://angular.io/api/core/computed中发布了计算值功能

uklbhaso

uklbhaso9#

基本上,在angular中,你不需要像Vuejs那样的计算属性。一旦移动到一个新的框架,你应该适应和利用新框架的功能和解决方案,忘记以前的。在angular中,您有几种在模板中操作数据的方法,但有一个规则。
避免从模板调用方法,如getter
相反,还有其他几种方法。
管道
可观测的
表达式
ngModel
您可以根据您的数据流和需求选择一个。

相关问题