0

我遇到了一个场景,我需要对 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;
  }
4

4 回答 4

1

我建议在withLatestFrom这里使用 rxjs 运算符。我将创建一个 Observable for numOfColumns,只要它更新,最终订阅就会触发。但是:它不会触发,直到您拥有来自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. 如果可以,您可以使用任何不可能的值对其进行初始化,然后通过管道对其进行过滤。

于 2020-05-09T19:52:28.920 回答
1

您可以添加另一个输入属性并将 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
    }
}
于 2020-05-09T20:00:48.557 回答
0

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

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

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

于 2020-05-09T19:48:11.050 回答
0

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())
    );
于 2020-05-09T19:55:51.927 回答