如何在 Angular 的 ngOnChanges 上访问 @Input 参数以外的变量?
How to access variables other than @Input params on ngOnChanges in Angular?
我遇到了一个场景,我需要对 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;
}
ngOnChanges 在 ngOnInit 之前首先被触发。因为 Inputs 是在 ngOnInit 之前填充的。
如果您需要来自 ngOnInit 的值,您可以在 ngOnChanges 中使用布尔值 "isInitDone"。并且不要忘记在您的 ngOnInit 中将此布尔值设置为 true。
但是,您还应该触发在 ngOnChanges 方法中完成的操作。所以也许把它放在一个方法中,ngOnChanges 和 ngOnInit 都会调用...
另一个解决方案是将 ngOnInit 代码移动到 ngOnChanges,在 if (this.breakpoint$)
我建议在这里使用 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
。如果可以,您可以使用任何不可能出现的值对其进行初始化,并在以后通过管道对其进行过滤。
Angular的组件生命周期钩子按以下顺序触发:
- ngOnChanges
- ngOnInit
- ngDoCheck
- ngAfterContentInit
- ngAfterContentChecked
- ngAfterViewInit
- ngAfterViewChecked
- 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())
);
您可以添加另一个输入 属性 并将 numOfCols$ 移动到 parent 组件中。
父母补偿HTML
<myParent [data]="list$ | async"
[numOfCols]="numOfCols$ | async>
</myParent>
ParentCom 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
}
}
我遇到了一个场景,我需要对 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;
}
ngOnChanges 在 ngOnInit 之前首先被触发。因为 Inputs 是在 ngOnInit 之前填充的。
如果您需要来自 ngOnInit 的值,您可以在 ngOnChanges 中使用布尔值 "isInitDone"。并且不要忘记在您的 ngOnInit 中将此布尔值设置为 true。
但是,您还应该触发在 ngOnChanges 方法中完成的操作。所以也许把它放在一个方法中,ngOnChanges 和 ngOnInit 都会调用...
另一个解决方案是将 ngOnInit 代码移动到 ngOnChanges,在 if (this.breakpoint$)
我建议在这里使用 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
。如果可以,您可以使用任何不可能出现的值对其进行初始化,并在以后通过管道对其进行过滤。
Angular的组件生命周期钩子按以下顺序触发:
- ngOnChanges
- ngOnInit
- ngDoCheck
- ngAfterContentInit
- ngAfterContentChecked
- ngAfterViewInit
- ngAfterViewChecked
- 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())
);
您可以添加另一个输入 属性 并将 numOfCols$ 移动到 parent 组件中。
父母补偿HTML
<myParent [data]="list$ | async"
[numOfCols]="numOfCols$ | async>
</myParent>
ParentCom 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
}
}