Как получить ширину элемента (DOM) в Angular2

есть несколько похожих тем, но я не смог найти подходящего ответа для своих нужд. Так что в 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>

Связано: https://github.com/angular/angular/issues/6515


person Kevin Regenrek    schedule 22.08.2016    source источник
comment
Изменение размера в зависимости от тока со звуками, похожими на бесконечный цикл.   -  person Günter Zöchbauer    schedule 22.08.2016
comment
нет, только после просмотра инициализации и при изменении размера окна - это должно быть хорошо   -  person Kevin Regenrek    schedule 22.08.2016


Ответы (4)


Я не знаю, как получить ширину из элемента хоста без доступа к 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 (но не после инициализации).

person Günter Zöchbauer    schedule 22.08.2016
comment
Есть ли заметная разница между подходом @HostBinding и Renderer? Я так понимаю обе версии подходят? О, и this.el.nativeElement.width всегда равен нулю... поэтому я тоже боролся с этим. Как насчет: this.renderer.setElementStyle(this.el, 'height', this.el.clientWidth+'px'); Это работает, но выглядит странно для меня... - person Kevin Regenrek; 22.08.2016
comment
Подход рендерера тоже хорош. Я считаю, что подход HostBinding легче читать. В вашем вопросе this.el.width вместо this.el.nativeElement.width. Я предполагаю, что resizeWorks вызывается до того, как компонент полностью отрендерится, иначе попробуйте getBoundingClientRect().width. - person Günter Zöchbauer; 22.08.2016
comment
this.el ссылается на el.nativeElement (см. конструктор). Хорошо, я придерживаюсь getBoundingClientRect().width. Благодарю вас! Последняя строка кода: this.renderer.setElementStyle(this.el, 'height', this.el.getBoundingClientRect().width+px); - person Kevin Regenrek; 22.08.2016
comment
Верно. Извините, пропустил это. Итак, getBoundingClientRect() сработало? - person Günter Zöchbauer; 22.08.2016

Я попробовал решение @GünterZöchbauer и усовершенствовал его. Вам не нужен HostListener, чтобы получить границы div. Как и в случае с «щелчком» или другим событием (событие) = «функция ()», эта функция будет запущена. Я надеюсь, что кто-то найдет это полезным.

<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) - это переменная, необходимая для получения цели измерения. Это не обязательно должно быть то же имя, что и элемент, это может быть любое имя.

Еще один способ получить размеры элемента — использовать класс ElementRef из Angular Core, но тогда вам нужно найти тот дочерний элемент, у которого есть свойства ширины и высоты. Я использовал это в 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
    };
}
person Imants Volkovs    schedule 01.12.2016
comment
Тоже хорошее решение! - person Kevin Regenrek; 01.12.2016

Я проверил этот способ и просто работал! Кажется, что связывание сделало эту работу.

<div #foto [style.height.px]="foto.getBoundingClientRect().width / 2">
person Charleston    schedule 06.09.2018
comment
Это очень умно. - person WebWanderer; 01.05.2020
comment
Это работает, но обнаружение изменений Angular заставит браузер запускать метод getBoundingClientRect каждый раз при обнаружении изменения. Если вы специально не хотите, чтобы стиль этого div обновлялся таким образом, вам следует вместо этого сохранить эту ширину в файле .ts и обновлять ее только при необходимости. - person HugoPrunaux; 02.02.2021

Вы можете получить ширину самого компонента, если для него определено свойство отображения (например, «блок» в приведенном ниже примере). Обратите внимание, что вы должны иметь возможность определить этот стиль (используя затенение дома):

:host {
    display:block;
}

Если вы не используете затенение дома (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;
    }
}
person Flavien Volken    schedule 08.08.2017