我有一个相对于 nativeElement.getBoundingClientRect()
定位自身的 Angular 组件 (5.2)另一个元素的输出。
在测试中,我创建了一个带有样式的包装器组件来模拟定位元素,但是返回的是 ClientRect
始终全为零。
有没有办法让 Angular 实际在 DOM 中定位我的元素?
为了说明,这是我的 TestComponent
.内部 <popup>
组件将使用 anchor.nativeElement.getBoundingClientRect()
计算它的固定位置.
我认为这不相关,但我正在使用 Jest 来执行测试。
我已经尝试过有和没有 async()
.
@Component({
selector: 'test',
styles: [`
:host {
width: 1000px;
height: 1000px;
display: flex;
justify-content: center;
align-content: center;
}
`],
template: `
<button #button></button>
<popup [anchor]="button">
<div id="helloWorld">hello world</div>
</popup>
`
})
class TestComponent {
@ViewChild('button')
public buttonElement: ElementRef;
}
describe('test', () => {
let fixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
imports: [PopupModule]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
});
}));
it('should be positioned', () => {
const button = fixture.componentInstance.buttonElement.nativeElement;
// Either of these always fail, because the button is not positioned.
// I have also tried using fixed positioning of the button.
// this will fail
expect(button.getBoundingClientRect().top).not.toEqual(0);
// this will fail, too
return fixture.whenRenderingDone().then(() => {
expect(button.getBoundingClientRect().top).not.toEqual(0);
});
});
});
请您参考如下方法:
长话短说:
您只需要在 ngAfterViewInit() Hook 中使用 ElementRef
类型的 nativeElement
,您的测试将“看到”DOM 中的元素。
长读:
.getBoundingClientRect()
只有在元素已呈现并存在于 DOM 中时才会起作用。今天,当我尝试访问被单击的元素时,我遇到了这种情况,并且使用 [routerLink]
它被路由器操作动态重新呈现 - 我无法在视觉上识别这种行为。它导致这个元素消失,并且不能有任何测量。接下来,在 ClientRect 中渲染了一些类似的元素,但为 0,0,0,0。
因此,如果您使用一些元素的动态重新呈现 - 如菜单或其他元素,您的 element.getBoundingClientRect()
将仅在元素消失时返回 0,0,0,0,重新渲染,或在“ Action 时间”(点击等)在 DOM 中不存在,就像您的情况一样。
因此您需要等待它被重新渲染和接下来 - 您可以通过 .getBoundingClientRect()
获得它的测量值。对于 @ViewChild()
最好的等待方式是 ngAfterViewInit()
Hook ,您可以在其中访问 nativeElement(作为最佳方式)。此外 - 还有一些技巧,例如:
private elementInited$ = new Subject();
public desiredElement: ElementRef;
@ViewChild('someElemRef') set setElementRef(el: ElementRef) {
if (!!el) {
this.desiredElement= el;
this.elementInited$.next();
}
}
ngOnInit() {
this.elementInited$.subscribe((inited) => {
// do stuff with nativeElement
const values = this.desiredElement.nativeElement.getBoundingClientRect() // <== works!
})
}
但这太棘手了:)
此外,等待元素的一个好方法是 @Directive()
。 使用指令,您可以以“Angular 方式”正确地监听元素,并且仅当元素存在于 DOM 中时才会触发指令。
在 Devtools 中使用 Chrome 的渲染亮点也可能非常有帮助 - 只需打开 devtools 菜单 - 更多工具 > 渲染 > Paint flashing
复选框有助于识别哪些元素重新渲染。