簡體   English   中英

Angular 在模板上顯示 model 計算數據的正確方法

[英]Angular proper way of displaying model calculated data on template

考慮一個Person model,例如:

class Person {
  firstName: string;
  lastName: string;
}

以及從中計算/派生的數據,例如`${firstName} ${lastName}`fullName (這只是一個示例,因為計算可能更復雜和繁重)。

在組件模板上顯示計算數據的正確方法是什么? 考慮到:

  • 我想盡可能地尊重 DRY,我不想重復我的 model 或在模板上寫很多次{{ person.firstName }} {{ person.lastName }}
  • 由於性能問題,即使使用OnPush更改檢測策略,在模板上使用 getter 或方法也不可行;
  • 計算邏輯必須留在model中,因為我需要在TS代碼的某個地方使用它,所以我不想使用純管道,或者建議的組件計算屬性(如果在某些時候會實現)或一些解決方法獲得類似的行為。

我考慮過對Person model 使用不可變的方法,並僅在構造函數中計算派生屬性:

class Person {
  firstName: string;
  lastName: string;
  readonly fullName: string;
  constructor(firstName: string, lastName: string) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.fullName = `${firstName} ${lastName}`;
  }
}

顯然,使用這種方法我不能做person.firstName = 'Changed'來更新fullName屬性,但我必須做person = new Person('Changed', person.lastName)
我已經考慮將readonly也應用於firstNamelastName以確保不變性模式,但是通過這種方式,我無法在另一個模板上下文中使用Person model,例如,我需要將firstNamelastName綁定到某些輸入楷模。

我真的很想知道正確處理這種常見情況的標准/正確/最佳實踐方法是什么。

我會考慮使用屬性(獲取)


class Person {
 firstName: string;
 lastName: string;
 
 get fullName(): string { return `${this.firstName} ${this.lastName}`;}

 constructor(firstName: string, lastName: string) {
   this.firstName = firstName;
   this.lastName = lastName;
 }
}

編輯

為了優化經常發生的變化並減少重新計算,您可以使用 Subject 和 pipe 它與 debounceTime。


class Person{

  private _firstName: string;
  private _lastName: string;
  private _fullName: string;
  private _debounceFirstname = new Subject<string>();
  private _debounceLastname = new Subject<string>();

  constructor(firstName: string, lastName: string) {
    this._firstName = firstName;
    this._lastName = lastName;
    this._debounceFirstname
           .pipe(debounceTime(300)).subscribe((value: string) => this.firstName = value);
    this._debounceLastname
           .pipe(debounceTime(300)).subscribe((value: string) => this.lastName = value);
    this.changeCalculation();
  }

  get firstName(): string {
    return this._firstName;
  }

  set firstName(value: string) {
    this._firstName = value;
    this.changeCalculation();
  }
  get lastName(): string {
    return this._lastName;
  }

  set lastName(value: string) {
    this._lastName = value;
    this.changeCalculation();
  }

  get fullName(): string{
    return this._fullName;
  }

  private changeCalculation(): void {
      this._fullName= `${this.firstName} ${this.lastName}`;
  }

}

我看到您想避免使用 getter 和方法,但我並不完全相信這種方法的性能缺點是如此昂貴。 一般來說,如果那些 getter 和方法不做一些繁重的工作,我認為你不應該放棄它們。 當然,與您的情況一樣,這些操作可能更復雜。

我處理這種情況的主觀方法是使用 getter 和 setter,同時盡可能避免fullName計算:

class Person {
  _firstName: string;
  _lastName: string;
  _fullName: string;

  constructor(firstName: string, lastName: string) {
    this.firstName = firstName;
    this.lastName = lastName;
    this._fullName = `${firstName} ${lastName}`; // First setting of the fullName
  }

  get firstName() {
    return this._firstName;
  }

  set firstName(newFirstName: string) {
    if (newFirstName !== this._firstName) {
      // Update the full name
      this._fullName = `${newFirstName} ${this._lastName}`;
      this._firstName = newFirstName;
    }
  }

  get lastName() {
    return this._lastName;
  }

  set lastName(newLastName: string) {
    if (newLastName !== this._lastName) {
      // Update the full name
      this._fullName = `${this._firstName} ${newLastName}`;
      this._lastName = newLastName;
    }
  }

  get fullName() {
    // Memoizing the full name
    if (!this._fullName) {
      // The full name is not going to be recalculated every time
      this._fullName = `${this._firstName} ${this._lastName}`;
    }
    return this._fullName;
  }
}

這是一個反應式的例子。 有些人認為在 RXJS 中使用 BehaviorSubject 有點反模式,但這是一個不同的討論。

模板代碼 - 使用異步 pipe 從 observable 中獲取值並管理訂閱。

<div>{{myPerson.fullName$ | async}}</div>

Model 代碼 - 定義名字和姓氏的行為主體。 使用 combinelatest 檢測名字或姓氏的更改。

class Person {
  private firstNameSubject: BehaviorSubject<string>;
  private lastNameSubject: BehaviorSubject<string>;
  fullName$: Observable<string>;
  constructor(firstName: string, lastName: string) {
    this.firstNameSubject = new BehaviorSubject(firstName);
    this.lastNameSubject = new BehaviorSubject(lastName);
    this.fullName$ = combineLatest([firstName, lastName]).pipe(
        map(([first, last]) => computeFullName(first, last)),
        shareReplay(1) //make our stream replayable to so we don't need to recompute the value per subscription
    );
  }
  setfirstName(newFirstName: string) {
    this.firstNameSubject.next(newFirstName);
  }
  setlastName(newLastName: string) {
    this.lastNameSubject.next(newLastName);
  }
  getFirstName() {
    return this.firstNameSubject.value;
  }
  getLastName() {
    return this.lastNameSubject.value;
  }

}
export function computeFullName(firstName: string, lastName: string) {
    return `${firstName} ${lastName}`;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM