首頁  >  問答  >  主體

Angular - 如何更新採用輸入參數並第一次正確渲染它的子元件

我的教育經驗如下:

<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粉463418483P粉463418483211 天前408

全部回覆(2)我來回復

  • P粉658954914

    P粉6589549142024-03-22 14:12:38

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

    回覆
    0
  • P粉022723606

    P粉0227236062024-03-22 13:08:51

    問題的根本原因是 @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
      }
    }

    深度克隆< /a>

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

    優點:

    缺點:

    • 需要額外的樣板來發送更改到子元件,也就是在父元件中建立多餘的 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)等

    回覆
    0
  • 取消回覆