我在使用两次触发的setter方法时遇到问题。我的子组件有一个表单,其中videoId
的设置值也会设置frame
的值。
还有一个输入允许您设置frame
。
当我通过html输入在子组件中设置videoId
时,子组件中frame
的setter会触发两次。我添加了一些逻辑来检查某些值以防止事件发射器(this.frameChange.emit(frame)
)触发两次,因为这会在父组件中进行API调用。这只是一个绑带,我已经达到了它的局限。
如果我在子组件的frame
setter中放置断点,则调用堆栈会在第一次触发时显示以下内容...
=> set frame.controls.component.ts:52
FrameControlsComponent.onSubmit frame.controls.component.ts:52`
View_FrameControlsComponent0.handleEvent_0 /SuperApp...factory.js:546
...
和第二次射击......
=> set frame.controls.component.ts:52
Wrapper_FrameControlsComponent.check_frame (/SuperAppModule/FrameControlsComponent/wrapper.ngfactory.js:31)
View_SuperAppComponent0.detectChangesInternal (/SuperAppModule/SuperAppComponent/component.ngfactory.js:393)
AppView.detectChanges (view.js:438)
DebugAppView.detectChanges (view.js:633)
AppView.internalDetectChanges (view.js:423)
View_SuperAppComponent_Host0.detectChangesInternal (/SuperAppModule/SuperAppComponent/host.ngfactory.js:30)
AppView.detectChanges (view.js:438)
DebugAppView.detectChanges (view.js:633)
ViewContainer.detectChangesInNestedViews (view_container.js:71)
View_Pages0.detectChangesInternal (/PagesModule/Pages/component.ngfactory.js:98)
AppView.detectChanges (view.js:438)
DebugAppView.detectChanges (view.js:633)
AppView.internalDetectChanges (view.js:423)
View_Pages_Host0.detectChangesInternal
...
(堆叠痕迹还有更多,请告诉我是否过早切断它们。)
我已经采取的处理这个问题的逻辑是不可持续的,我真的需要找到为什么这些事件发生两次的根源。我理解第一个电话的来源,但不是来自Wrapper_FrameControlsComponent.check_frame
以下是相关代码
<!-- PARENT HTML -->
<div class="card w-100 card-panel">
...
<frame-controls [(videoId)]="videoId" [(frame)]="frame"></frame-controls>
...
</div>
------
<!-- CHILD HTML -->
<input class="form-control" type="number" step="1" placeholder="ID"
id="videoId" name="videoId" #videoId="ngModel"
[ngModel]="model.videoId" (ngModelChange)="setVideoId($event)">
<input type="number" step="1" class="form-control" placeholder="Number"
id="frame" name="frame" #frame="ngModel"
[(ngModel)]="model.frame">
...
// PARENT COMPONENT
@Component({
selector: 'super-app',
...
host: {
'(window:keydown)': 'handleKeyDown($event)',
'(window:keyup)': 'handleKeyUp($event)'
}
})
export class SuperAppComponent implements AfterViewInit {
@ViewChild('container') private elContainer: ElementRef;
private _frame: number;
get frame(): number {
return this._frame;
}
set frame(frame: number) {
this._frame = frame;
}
constructor(@Inject(DOCUMENT) private document: HTMLDocument, ...) {
...
}
...
}
// CHILD COMPONENT
@Component({
selector: 'frame-controls',
...
})
export class FrameControlsComponent implements AfterViewInit {
@Output() frameChange = new EventEmitter<number>();
public model = {
videoId: undefined,
frame: undefined,
};
private _videoId: number;
@Input()
get videoId(): number {
return this._videoId;
}
set videoId(videoId: number) {
if (!_.isUndefined(videoId) && (videoId !== this._videoId)) {
this._videoId = videoId;
this.model.videoId = videoId;
this.videoIdChange.emit(videoId);
...
}
}
private _frame: number;
@Input()
get frame(): number {
return this._frame;
}
set frame(frame: number) {
if ( !_.isUndefined(frame) && this._frame != frame ) { // TODO WHY DOES THIS FIRE TWICE THIS LOGIC IS HACKY AND DOES NOT WORK FOR ALL SCENARIOS
this._frame = frame;
this.model.frame = frame + 1;
this.frameChange.emit(frame);
}
}
constructor(...) {
}
public onSubmit() {
if (this.videoId !== this.model.videoId) {
this.videoId = this.model.videoId;
}
if (this.frame !== this.model.frame) {
this.frame = this.model.frame - 1;
}
}
public setVideoId(videoId: number) {
this.model.videoId = videoId;
this.model.frame = 1;
}
public setFrame(frame: number) {
frame = _.clamp(frame, 1, this.maxFrames);
this.frame = frame - 1;
}
}