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


评论关闭
IT干货网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!