javascript动态省略号文本基于窗口大小角度2 +

时间:2018-06-06 15:29:41

标签: angular angular5 angular6

所以我有一张图表(图片是一个甘特图),各种大小的div,并且里面有文字像这样

<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>`

这个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
    };
}

因此,当用户调整窗口DOWN大小时,所有这些似乎都按预期工作。布尔标志得到设置,文本被压缩到2行(仍然适合容器div)然后它最终溢出到3行,在这种情况下minSquishedWidth被设置,因为{{1滚动高度大于父<p>高度,后者又将<div>布尔值设置为true,这反过来导致我的模板中的第二个squished使用工具提示进行渲染,省略号类集。

然而,当用户将窗口调整得更大时,div中的文本在省略(椭圆化??)之间切换为3行然后再返回并且看起来很粗糙。这似乎是因为如果用户过快地调整窗口大小,窗口调整大小事件不会触发,直到窗口停止调整大小片刻,因此<p>可以设置得多比它实际应该低。我试图通过允许minSquishedWidth增加来纠正这个问题,只要溢出仍然在上升,有效地&#34;学习&#34;它的正确位置。然后,如果用户继续调整大小而不刷新页面,则省略号行为将按预期运行...

任何人都知道如何让它无需进行培训&#34;?或者通过确保第一次设置实际minSquishedWidth

组件的行为如下所示 :

starts as

then gets squished at some point on resize down

then when the user resizes up, it toggles between this state and the first因为它正在学习&#34;实际的maxSquishWidth

1 个答案:

答案 0 :(得分:0)

我得到了它的工作。因为我所做的计算必然取决于它们呈现后的组件外观。我不得不让我的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();
    }
  }
}