динамический текст многоточия javascript на основе размера окна 2+ - PullRequest
0 голосов
/ 06 июня 2018

Итак, у меня есть диаграмма (изобразите диаграмму Ганта) с элементами разных размеров, и внутри них есть текст, подобный этому

<div  #arrowWrap 
  [routerLink]="data.link"
  class="chart-item block-arrow"
  [ngClass]="{
    'disabled' : data.disabled,
    'enabled' : !data.disabled
  }">
     <p #arrowText *ngIf="!squished">{{data.name}}</p>
     <p #arrowTextWithTooltip *ngIf="squished" matTooltip={{data.name}} class="ellipsis-text" >{{data.name}}</p>
</div>`

этот элемент должен поддерживать ширину 15% контейнерачтобы сохранить целостность диаграммы.

Я пытаюсь динамически установить логическое значение squished на основе того, когда текст <p> переполняет свой контейнер.Это компонент объекта

import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
const ELLIPSIS_CLASS = 'ellipsis-text';

@Component({
  selector: 'app-block-arrow',
  templateUrl: './block-arrow.component.html',
  styleUrls: ['./block-arrow.component.scss']
})
export class BlockArrowComponent implements OnInit {
  @ViewChild('arrowText') unsquishedP: ElementRef;
  @ViewChild('arrowTextWithTooltip') squishedP: ElementRef;
  @ViewChild('arrowWrap') outterDiv: ElementRef;

  @Input() data: any; // data to populate template

  maxSquishWidth = -1;  // viewport width that causes overflow of p
  squished = false;


  @Input() set viewportWidth(width) {
    if ( this.outterDiv) {
      this.determineSquished(this.outterDiv.nativeElement.children[0], width);
    }
  }

 determineSquished(el: any, width: number) {
    if (el) {
      const parent = el.offsetParent;
      // would only enter if the element overflowed it's container
      // the desired behavior is that text can fit onto 2 lines but not 3
      // for some reason the scroll height of the text is always 1 or 2 pixels longer
      // higher than the height of the parent div, thus the magic 6
      if ( el.scrollHeight > parent.clientHeight + 6 ) {
        if (this.maxSquishWidth < width) {
          this.maxSquishWidth = width;
        }
      }

      this.squished = (width <= this.maxSquishWidth);
    }
  }
}

свойство ввода width устанавливается с помощью службы, которая создает наблюдаемые выходные события из события изменения размера окна

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { fromEvent } from 'rxjs/Observable/fromEvent';
import { map, pluck, distinctUntilChanged } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable({
  providedIn: 'root'
})
export class WindowService {
    public window = new BehaviorSubject(null);

    public width;

    constructor() {
        let windowSize = new BehaviorSubject(getWindowSize());

        this.width = windowSize.pipe(pluck('width'), distinctUntilChanged());   // <- pluck as Observable<number> throwing errors

        fromEvent(window, 'resize').pipe(
          map(getWindowSize)
        ).subscribe(windowSize);
    }
}

function getWindowSize() {
    return {
        width: window.innerWidth
    };
}

Так что все этокажется, работает как ожидалось, когда пользователь изменяет размер окна ВНИЗ.Установлен логический флаг get, текст разбивается на 2 строки (которые по-прежнему помещаются в div контейнера), а затем в конечном итоге переполняется до 3 строк, и в этом случае устанавливается minSquishedWidth, поскольку высота прокрутки <p> больше, чемparent <div> height, который, в свою очередь, устанавливает логическое значение squished в true, что, в свою очередь, заставляет 2-й <p> в моем шаблоне отображаться с набором всплывающей подсказки и класса многоточия.

HOWEVER, когдапользователь изменяет размер окна больше, текст в divs переключается между многоточием (ellipsized ??) на 3 строки и обратно и выглядит брутто.Это, кажется, является результатом того факта, что, если пользователь изменяет размер окна слишком быстро, событие изменения размера окна не срабатывает, пока окно не перестанет изменяться в течение некоторого времени, таким образом, minSquishedWidth может быть установлено намного ниже, чем оно фактическидолжно быть.Я попытался исправить это, позволив minSquishedWidth увеличиваться, пока переполнение все еще происходит на пути вверх, эффективно «изучая» его правильную позицию.И затем, если пользователь продолжает изменять размер, не обновляя страницу, поведение многоточия действует как ожидалось ...

У кого-нибудь есть идея, как заставить его работать без необходимости "тренировать"?или, возможно, гарантируя, что фактический minSquishedWidth будет установлен в первый раз?

Поведение компонента выглядит следующим образом:

начинается как

затем сжимается в какой-то момент при уменьшении размера

, затем, когда пользователь изменяет размеры, он переключается между этим состоянием и первым , поскольку он «изучает» фактическоеmaxSquishWidth

1 Ответ

0 голосов
/ 06 июня 2018

Я получил его на работу.Поскольку вычисления, которые я делал, обязательно зависели от того, как компоненты выглядели ПОСЛЕ того, как они отображались.Я должен был позволить моей maxSquishWidth логике произойти и затем запустить Angular, чтобы выполнить новую проверку компонента на наличие изменений.Затем повторите, если он нашел.

следующее работало:

import { Component, Input, ViewChild, ElementRef, AfterViewChecked, ChangeDetectorRef } from '@angular/core';

const ELLIPSIS_CLASS = 'ellipsis-text';

@Component({
  selector: 'app-block-arrow',
  templateUrl: './block-arrow.component.html',
  styleUrls: ['./block-arrow.component.scss']
})
export class BlockArrowComponent implements AfterViewChecked {
  @ViewChild('arrowText') unsquishedP: ElementRef;
  @ViewChild('arrowTextWithTooltip') squishedP: ElementRef;
  @ViewChild('arrowWrap') outterDiv: ElementRef;

  @Input() data: any; // data to populate template

  maxSquishWidth = -1;  // viewport width that causes overflow of p
  squished = false;
  windowWidth;

  @Input() set viewportWidth(width) {
    this.windowWidth = width;
  }

  constructor(private ref: ChangeDetectorRef) { }

  ngAfterViewChecked() {
      // will update when new data is passed to the component's inputs
      this.determineSquished(this.outterDiv.nativeElement.children[0], this.windowWidth);
  }

  determineSquished(el: any, width: number) {
    if (el) {
      const parent = el.offsetParent;
      // would only enter if the element overflowed it's container
      if ( el.scrollHeight > parent.clientHeight + 6 ) {
        // should never change after first time set
        this.squished = true;
        if (this.maxSquishWidth < width) {
          this.maxSquishWidth = width;
        }
      }
      this.squished = (width <= this.maxSquishWidth);
      // since updating the maxSquishWidth can cause the view to be invalid for any width that is +1 from current maxSquishWidth
      // need to detect view changes in this scenario and renrender if it would have overflowed
      // THIS BEING THE MOST IMPORTANT CHANGE
      this.ref.detectChanges();
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...