angular - nativeElement.getBoundingClientRect() 在 Angular 测试中总是返回 0

标签 angular typescript jestjs

我有一个相对于 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 复选框有助于识别哪些元素重新渲染。

关于angular - nativeElement.getBoundingClientRect() 在 Angular 测试中总是返回 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48894574/

相关文章:

reactjs - 添加并执行脚本 react-testing-library 和 jest

angular - 以编程方式设置 <mat-select> 的值

angular - ngModel 不能用于通过父 formGroup 指令注册表单控件

javascript - 如何在 typescript 中突出显示选项卡菜单中的事件选项卡

reactjs - enzyme 浅渲染无法正常工作

reactjs - 使用 redux-mock-store 进行单元测试 - 如何使该单元测试通过?

angular - 升级到 angular9 问题 : Failed to find exported name of node

angular - 在二维数组中使用@viewchild

angular - 在 Angular 2 中获取 "Property X is missing in type error"

javascript - 无法使用super调用继承的类函数