Angular - 如何更新採用輸入參數並第一次正確渲染它的子元件
P粉463418483
P粉463418483 2024-03-21 21:06:55
0
2
472

我的教育經驗如下:

<cdk-virtual-scroll-viewport itemSize="5" class="list-scroll">
         <app-education-item *ngFor="let education of loadedEducations"
          (isSelected)="changeSelected(education)"
          [ngClass]="{ selected: education == loadedEducation }"
          [education]="education"
          (isRemoved)="removeEducation(education)"
        ></app-education-item>
      </cdk-virtual-scroll-viewport>

並且是以下元件

<div [ngClass]="{ 'list-item-container-collapsed' : isCollapsed, 'list-item-container': !isCollapsed, 'unselected': !isActive, 'selected': isActive}" (click)="selectEducation()">
    <div class="top-items-container" style="display: flex;">
     <div class="item-text">
     <span class="txt-header">{{educationHeader}}</span>
     <p class="txt-date"> 
         <span>{{startDate}}</span> - 
         <span>{{endDate}}</span>
     </p>
 </div>
</div>

具有以下邏輯,用於顯示從參數中取得的資料:

export class EducationItemComponent implements OnInit {

  @Input()
  education: Education;
  isCollapsed = false;
  isActive = false;
  startDate: string;
  endDate: string;
  educationHeader: string;
  educationDescription: string;

  constructor() { }

  ngOnInit(): void {
    console.log(this.education);
    this.startDate = this.education.startDate != '' ? formatDate(this.education.startDate, 'MMM yyyy', 'en-US')
        : formatDate(new Date(), 'MM YYYY', 'en-US') ;
    this.endDate = this.education.endDate != 'present' ? this.endDate = formatDate(this.education.endDate, 'MMM yyyy', 'en-US')
        : this.education.endDate;
    this.educationHeader = this.education.degree == undefined || this.education.description == undefined ? ''
        : this.education.degree + ' at ' + this.education.school;

    if (!this.education.description.enUS && this.education.description.nlNL) {
      this.educationDescription = this.education.description.nlNL;
    } else if (this.education.description.enUS) {
      this.educationDescription = this.education.description.enUS;
    }
}

我使用自訂事件來處理更新

@Output() updatedValue: EventEmitter<any> = new EventEmitter<string>();

  constructor() {}

  ngOnInit(): void {}

  fieldChanged(changes: SimpleChanges) {
    this.updatedValue.emit(changes);
  }

然後我有以下 html 用於操作資料:

<div class="update-wrap">
        <div class="list-header">Update education</div>
        <div>
          <div class="col-sm-6 input-wrapper">
            <app-input-field
              label="Institution"
              [value]="loadedEducation.school"
              (updatedValue)="loadedEducation.school = $event"
            ></app-input-field>
          </div>
          <div class="col-sm-6 input-wrapper date-picker-input">
            <app-input-field
              label="Degree"
              [value]="loadedEducation.degree"
              (updatedValue)="loadedEducation.degree = $event"
            ></app-input-field>
          </div>
        </div>
</div>

但是,欄位[value]="loadedEducation.school" (updatedValue)="loadedEducation.school = $event" 中的更新資料不會與子元件綁定,因此在刷新並取得之前不會顯示任何內容來自資料庫的資料。

我可以嘗試實現哪些可能性?

我嘗試實現 ngOnChanges,但沒有成功。

P粉463418483
P粉463418483

全部回覆(2)
P粉658954914

當您變更清單中項目的屬性時,loadedEducations 清單不會變更。嘗試重新整理清單(this.loadedEducations = returnedEducations)或在專案中使用狀態管理

P粉022723606

問題的根本原因是 @Input() 無法偵測到物件和陣列內部的更改,因為它們都是 引用類型。您的education 屬性是一個對象,因此在父元件中進行的直接改變屬性的變更(例如education.school = 'newValue' )不會觸發子元件的屬性@Input() education 的任何變更

有幾種方法可以解決這個問題,每種方法都有其優點和缺點:


僅傳遞您需要的屬性作為基元

parent.component.ts

education: Education = 

parent.component.html





child.component.ts

export class EducationItemComponent implements OnChanges {
  @Input() school: string;
  @Input() degree: string;

  ngOnChanges(changes: SimpleChanges): void {
    // will emit whenever .school or .degree is changed in the parent
  }
}

優點:

  • 使用簡單直觀,「正常」工作
  • 無需額外的樣板即可將變更傳送到子元件

缺點:

  • 需要額外的樣板來接收對子元件的變更。隨著 @Input 數量的增長,變得笨重
  • 您失去了父元件和子元件之間的語意耦合,它們實際上由共用介面(即 Education 介面)綁定
  • 如果屬性也是引用類型,則無法很好地擴展,在這種情況下,這些屬性也需要解壓縮並作為基元傳遞

更改時在父級中重建物件

parent.component.ts

education: Education = 

updateEducation(educationProps: Partial): Education {
  this.education = {
    ...this.education, // Note: You may want to 'deep clone' your object depending on how nested it is
    ...educationProps
  }
}

深度克隆

parent.component.html





child.component.ts

export class EducationItemComponent implements OnChanges {
  @Input() education: Education;

  ngOnChanges(changes: SimpleChanges): void {
    // will emit whenever updateEducation() is called in the parent
  }
}

優點:

  • 保留使用 Education 接口,保持父元件和子元件之間的語意耦合
  • 提倡使用不可變物件通常對物件來說是一個很好的實踐
  • 無需額外的樣板即可接收子元件的變更。

缺點:

  • 需要額外的樣板來發送更改到子元件,也就是在父元件中建立多餘的 updateEducation() 函數

將反應性元素傳遞到您的子元件中,例如 BehaviorSubject,並直接訂閱更改

parent.component.ts

educationSubject: BehaviorSubject = new BehaviorSubject(  )

updateEducation(educationProps: Partial): Education {
  const updatedEducation: Education = {
    ...this.education, // Note: You may want to 'deep clone' your object depending on how nested it is
    ...educationProps
  }
  this.educationSubject.next(updatedEducation}
}

parent.component.html





  
  

child.component.ts

export class EducationItemComponent implements OnChanges {
  @Input() educationSubject: BehaviorSubject;
}

child.component.html


  

{{ education.school }}

優點:

  • 完全控制事件發送/訂閱。這對於您希望觸發的任何其他副作用都是有益的
  • 可以輕鬆擴展以使用許多元件,例如將 educationSubject 放入服務中,並將相同的服務注入到任何需要它的元件中
  • 也提倡使用不可變物件
  • 無需額外的樣板即可接收子元件的變更

缺點:

  • 需要額外的樣板來發送更改到子元件,也就是在父元件中建立多餘的 updateEducation() 函數
  • 使用反應式程式碼的典型限制,例如僅透過串流進行變異、需要避免取消訂閱(如果不使用 | async)等
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板