【问题标题】:How to access variables other than @Input params on ngOnChanges in Angular?如何访问Angular中ngOnChanges上的@Input参数以外的变量?
【发布时间】:2020-08-25 09:33:30
【问题描述】:

我遇到了一个场景,我需要对 ngOnchanges 上的数据进行一些处理(因为数据来自父组件的 @Input 数据绑定属性)。在此转换期间,我需要使用在 ngOnInit 中初始化的属性。我尝试直接使用它,但值未定义。有人可以让我知道我该如何实现这一目标。下面是我的场景的示例代码。

ParentComp HTML
<myParent [data]="list$ | async"></myParent>

ChildComp TS
export class {
  @Input() data : any ; //data from parent
  breakPoint$: Observable<number>

  constructor(){}
  ngOnInit(){
   this.breakPoint$ = fromEvent(this.window, 'resize').pipe(
      map(() => this.numOfCols())
    );
  }

ngOnChanges(changes: SimpleChanges) {
    if (changes['data']) {
       // need to do some data processing here based on numOfColumns value, so trying to access
       this.breakPoint$.pipe(

       ) // Here i get cannot read property pipe of undefined error
      );

 numOfCols(): number {
    return this.breakpointObserver.isMatched(`(max-width: 1008px)`) ? 4 : 5;
  }

【问题讨论】:

    标签: angular ngonchanges


    【解决方案1】:

    我建议在这里使用withLatestFrom rxjs 运算符。每当更新时,我将为numOfColumns 创建一个 Observable 最终订阅将触发。但是:它不会触发,直到您拥有来自ngOnInit()(第二个 Observable)的数据,并且只会触发 如果numOfColumns在此之后更新,即BreakPoint的后续更改不会触发订阅,如果您希望两者都触发,则可以使用zip

    我在setter 中这样做,您也可以使用ngOnChanges

    _numOfColumns;
    numOfColumnsObs$ = new BehaviorSubject(false);
    
    set numOfColumns(val) {
        this._numOfColumns = val;
        this.numOfColumnsObs$.next(val);
    }
    
    get numOfColumns() {
        return this._numOfColumns
    }
    
    ngOnInit() {
        this.breakPoint$ = fromEvent(this.window, 'resize').pipe(
            map(() => this.numOfCols())
            );
    
            // new code here.
            this.numOfColumnsObs$.pipe(withLatestFrom(this.breakPoint$))
            .pipe(filter(([columns]) => columns !== false))
            .subscribe(([columns, breakPoint]) => {
                // do the changes you wanted in `ngOnChanges`;
            })
    }
    

    这里需要一个 BehaviorSubject,因为 observable 会在订阅之前发出。既然有一个BehaviorSubject,就有一个hack,我用false初始化了它。假设 numOfColumns 永远不会以false 出现。如果可以,您可以使用任何不可能的值对其进行初始化,然后通过管道对其进行过滤。

    【讨论】:

    • 为什么不使用 BehaviorSubject 来处理这种行为?
    • @Random:是的,你说得对,没有 BehaviorSubject 就不行,让我更新一下。
    • @Asish 非常感谢。这是一个很好的解释。这对我来说是一个很好的学习。我也会尝试这个解决方案。
    【解决方案2】:

    您可以添加另一个输入属性并将 numOfCols$ 移动到父组件中。

    ParentComp HTML

    <myParent [data]="list$ | async"
              [numOfCols]="numOfCols$ | async>
    </myParent>
    

    家长委员会 TS

    numOfCols$: Observable<number>;
    
    constructor(private breakpointObserver: BreakpointObserver) {
       this.numOfCols$ = breakpointObserver.observe('(max-width: 1008px)').pipe(
          map(({matches}) => matches ? 4 : 5)
       );
    }
    

    ChildComp TS

    @Input() data: any;
    @Input() numOfCols: number;
    
    ngOnChanges(changes: SimpleChanges) {
        if (changes['data']) {
            // You can use this.numOfCols here
        }
    }
    

    【讨论】:

    • 确实,使用“断点服务”会好很多!好主意!
    • @Nifiro 和随机,谢谢。我正在考虑这样做,但是我错过了检查触发的生命周期钩子的顺序并提出了问题。
    【解决方案3】:

    Angular 的组件生命周期钩子按以下顺序触发:

    1. ngOnChanges
    2. ngOnInit
    3. ngDoCheck
    4. ngAfterContentInit
    5. ngAfterContentChecked
    6. ngAfterViewInit
    7. ngAfterViewChecked
    8. ngOnDestroy

    如您所见,您正在尝试访问未在第一个 ngOnChanges 中定义的属性。您可以像这样在 if 语句中简单地添加一个检查:

    ngOnChanges(changes: SimpleChanges) {
        if (changes['data'] && !!this.breakPoint$) {
    ....
    

    或者您甚至可以在 ngOnInit 之外定义 breakPoint$,如下所示:

       @Input() data : any ; //data from parent
       breakPoint$: Observable<number> = fromEvent(this.window, 'resize').pipe(
          map(() => this.numOfCols())
        );
    

    【讨论】:

      【解决方案4】:

      ngOnChanges 在 ngOnInit 之前首先触发。因为输入是在 ngOnInit 之前填充的。
      如果您需要来自 ngOnInit 的值,您可以在 ngOnChanges 中使用布尔值“isInitDone”。并且不要忘记在您的 ngOnInit 中将此布尔值设置为 true。

      但是,您还应该触发 ngOnChanges 方法中所做的事情。所以也许把它放在一个方法中,ngOnChanges 和 ngOnInit 都会调用......

      另一种解决方案是将 ngOnInit 代码移动到 ngOnChanges,在 if (this.breakpoint$)

      【讨论】:

        猜你喜欢
        • 2019-08-24
        • 2017-01-28
        • 2020-10-30
        • 2012-04-18
        • 1970-01-01
        • 1970-01-01
        • 2019-09-15
        • 2014-07-11
        • 1970-01-01
        相关资源
        最近更新 更多