在Angular2中如何获取(DOM)元素的宽度

36

有一些类似的线程,但我找不到适合我的需求的答案。因此,在angular2中应严格避免直接访问DOM。我只是想知道最佳实践是什么。

我想要实现的是根据当前宽度调整元素大小。

workitemresize.component.ts

import { Directive, ElementRef, HostListener, Renderer, Input } from '@angular/core';

@Directive({
  selector: '[workItemResize]'
})

export class WorkItemResizeDirective implements ngAfterViewInit {

  private el: HTMLElement;
  private renderer: Renderer;

  constructor(el: ElementRef, public renderer: Renderer)
  { 
    this.el = el.nativeElement; 
    this.renderer = renderer;
  }  


  @HostListener('window:resize', ['$event.target']) 
  onResize() 
  { 
    this.resizeWorks();
  }

  ngAfterViewInit() {
    this.resizeWorks();
  }

  private resizeWorks(): void {
    this.renderer.setElementStyle(this.el, 'height', this.el.width); // <-- doesn't work I kow that this.el.width doesn't exist but just for demonstration purpose
    this.renderer.setElementStyle(this.el, 'height', '500'); // <-- works
  }

}

projects.template.html

<div class="posts row">
        <div class="work-item col-xs-12 col-sm-6 col-no-padding"  workItemResize *ngFor="let post of posts">

                <!-- show some fancy image -->
                <div class="img"  [ngStyle]="{'background-image':'url('+post.better_featured_image.source_url+')'}"></div>

        </div>
</div>

Related: https://github.com/angular/angular/issues/6515


基于当前宽度调整大小听起来像一个无限循环。 - Günter Zöchbauer
不要在初始化视图和窗口调整大小后添加代码 - 这样就可以了。 - Kevin Regenrek
4个回答

25

我不知道有什么方法可以在不访问nativeElement的情况下获取宿主元素的宽度,但是设置可以通过以下方式完成:

@HostListener('window:resize', ['$event.target']) 
onResize() { 
  this.resizeWorks();
}

@HostBinding('style.height.px')
elHeight:number;

private resizeWorks(): void {
  this.elHeight = this.el.nativeElement.width;
}

如果您可以在组件模板中添加一个元素,例如:
<div style="width: 100%;" #div (window:resize)="elHeight = div.getBoundingClientRect()">
  <!-- your template here -->
</div>

那么这样做就可以完全不需要直接访问DOM(但不能在初始化之后)。

@HostBinding和Renderer方法之间有什么显著的区别吗?我认为两个版本都可以。 哦,还有this.el.nativeElement.width总是null...所以我也在苦苦挣扎。那么这样怎么样:this.renderer.setElementStyle(this.el, 'height', this.el.clientWidth+'px'); 它能工作,但对我来说看起来有点奇怪... - Kevin Regenrek
渲染器方法也可以。我发现HostBinding方法更易于阅读。你的问题中有this.el.width而不是this.el.nativeElement.width。我假设resizeWorks在组件完全渲染之前被调用,否则尝试使用getBoundingClientRect().width - Günter Zöchbauer
1
this.el 是指 el.nativeElement(参见构造函数)。好的,我会坚持使用 getBoundingClientRect().width。谢谢!最终代码行: this.renderer.setElementStyle(this.el, 'height', this.el.getBoundingClientRect().width+"px"); - Kevin Regenrek
好的。抱歉,我漏掉了那个。所以 getBoundingClientRect() 起作用了? - Günter Zöchbauer

15

我尝试了@GünterZöchbauer的解决方案并进行了改进。您不需要使用HostListener来获取div的边界。就像使用“click”或其他事件(event)=“function()”一样,它将触发此函数。我希望有人会发现这有帮助。

<div #div (window:resize)="onResize(div.getBoundingClientRect())">
   <!-- your template here -->
</div>


onResize() { 
   this.resizeWorks();
}

@HostBinding('style.height.px') elHeight:number;

private resizeWorks(): void {
   this.elHeight = this.el.nativeElement.width;
}

(#div) - 这是获取测量目标所需的变量。它不必与元素名称相同,可以是任何名称。

另一种获取元素尺寸的方法是使用 Angular Core 的 ElementRef 类,但是需要找到具有宽度和高度属性的子元素。我在 Angular 2 Maps 中使用了这种方法来获取容器的宽度和高度,但我发现,使用这种方法我必须在元素树中查找具有这些属性的元素,它是根元素的第二个子元素。这更加混乱。 ElementDimensions - 只是一个包含高度和宽度属性的类,我用它来存储这些属性。

<div (window:resize)="onResize()">
   <!-- your template here -->
</div>

private getContainerDimensions(): ElementDimensions{
    var rootElement = this.elementRef.nativeElement;
    var childElement = rootElement.firstElementChild;
    var contentElement =  childElement.firstElementChild;

    return {
        height:contentElement.clientHeight,
        width: contentElement.clientWidth
    };
}

也是一个不错的解决方案! - Kevin Regenrek

8

我尝试了这种方法,它很简单有效!似乎是绑定实现了这个任务。

<div #foto [style.height.px]="foto.getBoundingClientRect().width / 2">

这非常聪明。 - WebWanderer
3
这个方法可以工作,但是Angular的变更检测会在每次检测到变化时运行getBoundingClientRect方法。除非你特别希望以这种方式更新div的样式,否则你应该将宽度存储在.ts文件中,并且只在需要时更新它。 - HugoPrunaux

4

如果组件本身定义了display属性(如下面的示例中的“block”),则可以获取其宽度。请注意,您应该能够定义此样式(使用dom shadowing):

:host {
    display:block;
}

如果您没有使用DOM阴影技术(Ionic 2-3):
my-component {
    display:block;
}

这是控制器(将宽度作为可观察对象公开):

import {AfterViewInit, Component, ElementRef, HostListener, Input} from '@angular/core';
import {Subject} from 'rxjs/Subject';

@Component({
    selector: 'my-component',
    template: '{{$width|async}}',
    style: 'my-component {display: block;}'
})
export class MyComponent implements AfterViewInit {
    @Input() public gridWidth: number;
    private $width: Subject<number> = new Subject();

    constructor(private elementRef: ElementRef) {
    }

    public ngAfterViewInit(): void {
        this.emitWidth();
    }

    @HostListener('window:resize', ['$event.target'])
    public onResize(): void {
        this.emitWidth();
    }

    private emitWidth(): void {
        this.$width.next(this.getWidth());
    }

    private getWidth(): number {
        return this.getNativeElement().clientWidth;
    }

    private getNativeElement(): HTMLElement {
        return this.elementRef.nativeElement;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接