首页 > 解决方案 > 为什么在修改类的本地属性时会执行 ngOnChanges?

问题描述

我已经处理这种情况一段时间了,我提前感谢您的建议

ngOnChanges在我理解它不应该运行的上下文中运行。当修改最初通过@Input. 这种修改会导致ngOnchanges钩子在一个上下文中执行,而不是在另一个上下文中执行。我在下面描述我的场景

我有以下父组件,其中包含传递给子组件的客户列表,

父控制器

export class AppComponent {
  customers: ICustomer[];
  currentlySelected: Option = 'All';

  constructor() {
    this.customers = [
      {
        id: 1,
        name: 'Task1',
        status: 'Pending',
      },
      {
        id: 2,
        name: 'Task2',
        status: 'Pending',
      },
      {
        id: 3,
        name: 'Task3',
        status: 'Progress',
      },
      {
        id: 4,
        name: 'Task4',
        status: 'Closed',
      },
    ];
  }

  selectBy(option: Option): void {
    this.currentlySelected = option;
  }

  filterBy(): ICustomer[] {
    if (this.currentlySelected === 'All') {
      return this.customers;
    }

    return this.customers.filter(
      (customer) => customer.status === this.currentlySelected
    );
  }
}

父模板

<nav>
  <ul>
    <li (click)="selectBy('All')">All</li>
    <li (click)="selectBy('Pending')">Pending</li>
    <li (click)="selectBy('Progress')">Progress</li>
    <li (click)="selectBy('Closed')">Closed</li>
  </ul>
</nav>

<app-list [customers]="filterBy()"></app-list>

在将客户传递给子组件之前,它们会根据客户状态属性进行过滤,这就是该filterBy函数的目的。

钩子中的子组件ngOnChanges通过添加属性来修改每个客户showDetail并为其分配值false

export class ListComponent implements OnInit, OnChanges {
  @Input() customers: ICustomer[] = [];

  constructor() {}

  ngOnChanges(changes: SimpleChanges): void {
    this.customers = changes.customers.currentValue.map(
      (customer: ICustomer) => ({
        ...customer,
        showDetail: false,
      })
    );
  }

  ngOnInit(): void {
    console.log('init');
  }

  toggleDetail(current: ICustomer): void {
    this.customers = this.customers.map((customer: ICustomer) =>
      customer.id === current.id
        ? { ...customer, showDetail: !customer.showDetail }
        : { ...customer }
    );
  }
}

调用该toggleDetail方法会更改showDetail属性的值以显示客户的详细信息

子模板

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <ng-container *ngFor="let customer of customers">
      <tr>
        <td>{{ customer.name }}</td>
        <td>
          <button (click)="toggleDetail(customer)">Detail</button>
        </td>
      </tr>
      <tr *ngIf="customer.showDetail">
        <td colspan="2">
          <pre>
            {{ customer | json }}
          </pre>
        </td>
      </tr>
    </ng-container>
  </tbody>
</table>

发生的行为如下,当列出所有客户端并单击详细信息时,它按预期工作,但是如果我更改为另一个状态并且列表已更新并且我单击详细信息,它不会显示详细信息。原因是ngOnchanges钩子被重新执行导致showDetail属性再次设置为 false,从而违背了我的意图。

为什么ngOnChanges在这种情况下执行?有什么办法可以解决?

更新 1

我添加了示例应用程序:https ://stackblitz.com/edit/angular-ivy-dkvvgt?file=src/list/list.component.html

标签: javascriptangular

解决方案


你的代码中有

<app-list [customers]="filterBy()"></app-list>

Angular 在执行之前无法知道函数“filterBy”的结果,因此每次检查时都会执行它。这就是我们应该避免在 .html 中使用函数的原因(当然,我们可以使用,但我们需要考虑到这是有代价的)。使用辅助变量

customerFilter:ICustomer[];

在构造函数中添加

constructor() {
    this.customers = [...]
    this.customerFilter=this.customers //<--add this line
  }

并在 selectBy

  selectBy(option: Option): void {
    this.currentlySelected = option;
    this.customerFilter=this.filterBy() //<--this line
  }

现在将 customerFilter 作为参数传递

<app-list [customers]="customerFilter"></app-list>

你的分叉堆栈闪电战


推荐阅读