繁体   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