![](/img/trans.png)
[英]How to solve Cannot read property 'nativeElement' of undefined in angular when using viewchild ElementRef
[英]Using @ViewChild { read: ElementRef } of component causes unit test to fail
在我的组件中,有一个子组件,如下所示:
<child-component #childComponent></childComponent>
然后,在父组件中,我使用@ViewChild和read
参数访问此子组件以获取ElementRef,而不是组件引用。 我需要的ElementRef,以确保我能得到一些属性nativeElement
,我需要。 就像这样:
export class ParentComponent {
@ViewChild('childComponent', { read: ElementRef }) public childComponent: ElementRef;
public position: string;
// some way down the code
private someMethod() {
if (this.childComponent.nativeElement.offsetLeft > 500) {
this.position = 'left';
} else {
this.position = 'right';
}
}
}
因此,这适用于该应用程序,但是我正在编写测试并模拟子组件,如下所示:
@Component({
selector: 'child-component',
template: ''
})
class ChildComponentMockComponent {
private nativeElement = {
get offsetLeft() {
return 600
}
};
}
beforeEach(async(() => TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ParentComponent, ChildComponentMockComponent ],
providers: [ ... ],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents()));
it('should have the correct position, based on position of child-component', () => {
spyOn(component, 'someMethod');
expect(component.someMethod).toHaveBeenCalled();
expect(component.position).toBe('left');
});
因此,测试将编译组件,并使用this.position
子组件值作为适当值,并计算this.position
的值,然后在测试this.position
其断言。
但是,设置了{ read: ElementRef }
参数后,即使已将其添加到声明数组中,TestBed也会完全忽略该模拟。 如果删除{ read: ElementRef }
,则该模拟将在测试中使用并通过。 但是然后我的应用程序不起作用,因为它现在正在获取组件引用,而nativeElement
属性不存在,而不是元素引用。
那么,如何在应用程序中获取ElementRef,然后在测试中使用模拟组件呢?
我已经通过更改应用程序的体系结构来解决此问题。 现在,子组件将找到其自己的offsetLeft属性,然后将其放入输出EventEmitter中以获取父组件。
export class ChildComponent implements AfterViewInit {
@Output() offsetPosition: EventEmitter<number> = new EventEmitter<number>();
constructor(private el: ElementRef) {}
public ngAfterViewInit() {
this.offsetPosition.emit(this.el.nativeElement.offsetLeft);
}
}
export class ParentComponent implements AfterViewInit {
public childComponentOffset: number;
public ngAfterViewInit() {
setTimeout(() => {
// this.childComponentOffset is available
// needs to be in setTimeout to prevent ExpressionChangedAfterItHasBeenCheckedError
// more info: https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
}
}
public getChildComponentOffset(position: number): void {
this.childComponentOffset = position;
}
}
然后在HTML中,您只需使用输出变量和方法定义子组件:
<child-component (offsetPosition)="getChildComponentOffset($event)"></child-component>
在测试中,然后为子组件模拟ElementRef并将其用作提供程序。
const mockElementRef: any = {
get offsetLeft() {
return position;
}
};
beforeEach(async(() => TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ ParentComponent ],
providers: [
{ provide: ElementRef, useValue: mockElementRef }
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents()));
it('should have the correct position, based on position of child-component', (done) => {
component.getChildComponentOffset(600);
setTimeout(() => expect(component.position).toBe('left'));
done();
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.