簡體   English   中英

Angular 手動設置的@Input 值在ngOnChanges 中仍然被相同的值覆蓋

[英]Angular manually set @Input value is still overwritten with the same value in ngOnChanges

我有一個父組件employees.component.ts和一個子組件employee.component.ts 父母的模板分為兩部分——寬度的 20% 是一個 PrimeNG 樹組件(它的節點代表不同的員工),rest 是給孩子的。 孩子的模板是一種表格,您可以在其中更改所選員工的數據或添加新員工。 從樹中選擇一個節點時,雇員的 ID 作為輸入從父節點發送到子節點。 子組件中的輸入更改觸發ngOnChanges ,其中對員工數據發出 HTTP 請求。

現在,當我嘗試保存新員工時,我將該輸入設置為undefined ,以指示當前顯示的是一個空表單,以便可以創建新員工。 保存新員工后,我從響應中獲得創建的員工。 我將輸入設置為新員工的 ID 並用其數據填寫表格。

同樣在創建新員工之后,我向父級發送一個 output 以通知它,必須將一個新員工節點添加到樹中以表示這個新員工,並且 select 它是因為表格中填充了員工的數據。 問題是,當父母將節點設置為選中時,員工的 ID 作為輸入發送給孩子(在保存過程后當前undefined ),這再次觸發ngOnChanges並發出 HTTP 請求,但正如我得到的數據已經來自 POST 請求的響應,我不想發出這個新請求。 為避免這種情況,我在收到響應后立即手動設置輸入。 但是,即使現在輸入的值正確地是在樹中選擇的員工的 ID,ngOnChanges 仍然會被觸發。 當我將changes: SimpleChanges object 記錄到控制台時,我看到輸入的先前值顯示為undefined ,其當前值是正確的 ID。

為什么以前的值顯示為undefined ,即使我已經在其他任何事情之前設置它(output 給父級)? 我該怎么做才能不觸發 ngOnChanges?

覆蓋組件內的 @Input 屬性是一種反模式,因為它會導致各種難以跟蹤的意大利面條式信息流通過您的應用程序。

要解決此問題,您可以添加類型為BehaviorSubject的中介 class 成員,例如下面的(簡化)示例。

TS

@Component({ .. })
export class MyComponent implements OnChanges {
  /** single source of truth of your employeeId */
  private readonly employeeId_ = new BehaviorSubject<string>("");

  /** expose observable, that can be used in html with simple async pipe for efficient change detection */
  public readonly employeeId$ = this.employeeId_.asObservable();

  /** primary input, will detect changes from upstream parent */
  @Input() public employeeId(newValue: string) {
    this.setEmployeeIdValue(newValue);
  }

  /** lifecycle hook that detects changes from upstream and propagates them internally */
  ngOnChanges(changes: SimpleChanges) {
    if (changes.employeeId) {
      this.employeeId_.next(changes.employeeId.currentValue);
    }
  }

  /** method to synchronously evaluate latest value */
  public getEmployeeIdValue(): string {
    return this.employeeId_.getValue();
  }

  /** call from your html or from this component's code to handle your business/ui logic */
  public setEmployeeIdValue(newValue: string) {
    this.employeeId_.next(newValue);
  }
}

HTML

<div>
  {{ employeeId$ | async }}
</div>

在 RxJS 文檔中閱讀有關 BehaviorSubject 的更多信息。

你有兩個問題:

  1. 為什么以前的值undefined
  2. 如何在創建新員工后立即在您的ngOnChanges中觸發呼叫

為什么以前的值undefined

您描述的行為是正確的。

創建新員工時,沒有員工 ID( undefined ),然后在填寫表格后調用創建新員工並在響應中接收員工 ID。

如果您向父組件發出新 ID,這會將其設置為您孩子的@Input ,那么將調用ngOnChanges並且您將正確地將previousValue設置為undefined並將currentValue設置為新 ID,這就是SimpleChange應該如何工作。

如何在創建新員工后立即在您的ngOnChanges中觸發呼叫

為避免為您剛剛創建的員工打電話,您可以在ngOnChanges中添加額外的檢查。 如果您從 parent 收到的 ID 與上次創建的員工的 ID 相同,則跳過加載調用:

@Component({
    selector: 'app-employee',
    templateUrl: './app-employee.component.html',
    styleUrls: ['./app-employee.component.scss']
})
export class EmployeeComponent implements OnChanges {
    @Input() 
    selectedEmployeeId: string;

    @Output()
    createdEmployee = new EventEmitter<string>();

    private createdEmployeeId: string;

    constructor(private http: HttpClient) {}

    ngOnChanges(changes: SimpleChanges): void {
        if (typeof changes.selectedEmployeeId.currentValue === 'undefined') {
            // there is no ID, cannot load employee data
            return;
        }

        if (changes.selectedEmployeeId.currentValue === this.createdEmployeeId) {
            // skip the call and reset createdEmployeeId to null
            this.createdEmployeeId = null;
        } else {
            // do a call to load selectedEmployeeId data
            this.http
                .get(...)
                .subscribe(...);
        }
    }

    createEmployee(): void {
        this.http
            .post(...)
            .subscribe(
                employeeId => {
                    this.createdEmployeeId = employeeId;
                    this.createdEmployee.next(employeeId);
                }
            )
    }
}

暫無
暫無

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

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