Angular 2阅读更多指令

时间:2016-06-14 18:14:02

标签: angular angular2-template angular2-directives

我需要在Angular2中构建一个readmore指令。这个指令将用于崩溃和扩展长文本块,并且#34;阅读更多"和"关闭"链接。不是基于字符数,而是基于指定的最大高度。

<div read-more [maxHeight]="250px" [innerHTML]="item.details">
</div>

任何人都可以指导在这种特定情况下获取/设置元素高度的最可靠方法。

关于如何实施这一特定指令的任何指导方针也将受到高度赞赏。

我需要构建像https://github.com/jedfoster/Readmore.js

这样的东西

解决方案:

在Andzhik的帮助下,我能够构建符合我要求的以下组件。

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

@Component({
    selector: 'read-more',
    template: `
        <div [innerHTML]="text" [class.collapsed]="isCollapsed" [style.height]="isCollapsed ? maxHeight+'px' : 'auto'">
        </div>
            <a *ngIf="isCollapsable" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}}</a>
    `,
    styles: [`
        div.collapsed {
            overflow: hidden;
        }
    `]
})
export class ReadMoreComponent implements AfterViewInit {

    //the text that need to be put in the container
    @Input() text: string;

    //maximum height of the container
    @Input() maxHeight: number = 100;

    //set these to false to get the height of the expended container 
    public isCollapsed: boolean = false;
    public isCollapsable: boolean = false;

    constructor(private elementRef: ElementRef) {
    }

    ngAfterViewInit() {
        let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight;
       //collapsable only if the contents make container exceed the max height
        if (currentHeight > this.maxHeight) {
            this.isCollapsed = true;
            this.isCollapsable = true;
        }
    }
}

用法:

<read-more [text]="details" [maxHeight]="250"></read-more>

如果有任何改进,请随时提出建议。

10 个答案:

答案 0 :(得分:21)

我制作了一个使用字符长度而不是div大小的版本。

import { Component, Input, ElementRef, OnChanges} from '@angular/core';

@Component({    
    selector: 'read-more',
    template: `
        <div [innerHTML]="currentText">
        </div>
            <a [class.hidden]="hideToggle" (click)="toggleView()">Read {{isCollapsed? 'more':'less'}}</a>
    `
})

export class ReadMoreComponent implements OnChanges {
    @Input() text: string;
    @Input() maxLength: number = 100;
    currentText: string;
    hideToggle: boolean = true;

    public isCollapsed: boolean = true;

    constructor(private elementRef: ElementRef) {

    }
    toggleView() {
        this.isCollapsed = !this.isCollapsed;
        this.determineView();
    }
    determineView() {
        if (!this.text || this.text.length <= this.maxLength) {
            this.currentText = this.text;
            this.isCollapsed = false;
            this.hideToggle = true;
            return;
        }
        this.hideToggle = false;
        if (this.isCollapsed == true) {
            this.currentText = this.text.substring(0, this.maxLength) + "...";
        } else if(this.isCollapsed == false)  {
            this.currentText = this.text;
        }

    }
    ngOnChanges() {
        this.determineView();       
    }
}

用法:

<read-more [text]="text" [maxLength]="100"></read-more>

答案 1 :(得分:17)

我认为您需要Component而不是DirectiveComponents更有意义,因为您需要添加了解更多按钮/链接,即更新DOM。

@Component({
    selector: 'read-more',
    template: `
        <div [class.collapsed]="isCollapsed">
            <ng-content></ng-content>
            <div (click)="isCollapsed = !isCollapsed">Read more</div>
        </div>
    `,
    styles: [`
        div.collapsed {
            height: 250px;
        }
    `]
})

export class ReadMoreComponent {
    isCollapsed = true;
}

用法:

<read-more>
   <!-- you HTML goes here -->
</read-more>

答案 2 :(得分:15)

在Andzhik的帮助下,我能够构建符合我要求的以下组件。

import maya.cmds as cmds

#Put in your material names here.  Make sure they have the EXACT SAME spelling, caps, 
#and name conventions as in the Hypershade.  And don't forget to put each one in ''!

matName = ['blue_mat','green_mat','red_mat','purple_mat']

cmds.shadingNode('surfaceShader',asShader=True,n='WhiteMat')
cmds.setAttr('WhiteMat.outColor', 1.0, 1.0, 1.0, type = 'double3')
cmds.shadingNode('surfaceShader',asShader=True,n='BlackMat')
cmds.setAttr('BlackMat.outColor', 0.0, 0.0, 0.0, type = 'double3')

for i in range(len(matName)):
    cmds.select(cl=True)
    cmds.select( ado=True)
    cmds.createRenderLayer(n=matName[i]+'_layer')

    cmds.hyperShade(objects=matName[i])
    cmds.editRenderLayerGlobals(crl=matName[i]+'_layer')    

    for x in range(len(matName)):
        if matName[x]!=matName[i]:
            cmds.hyperShade(objects=matName[x])
            cmds.hyperShade(assign='BlackMat')
        else:
           cmds.hyperShade(objects=matName[x])
           cmds.hyperShade(assign='WhiteMat')  

用法:

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

@Component({
    selector: 'read-more',
    template: `
        <div [innerHTML]="text" [class.collapsed]="isCollapsed" [style.height]="isCollapsed ? maxHeight+'px' : 'auto'">
        </div>
            <a *ngIf="isCollapsable" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed? 'more':'less'}}</a>
    `,
    styles: [`
        div.collapsed {
            overflow: hidden;
        }
    `]
})
export class ReadMoreComponent implements AfterViewInit {

    //the text that need to be put in the container
    @Input() text: string;

    //maximum height of the container
    @Input() maxHeight: number = 100;

    //set these to false to get the height of the expended container 
    public isCollapsed: boolean = false;
    public isCollapsable: boolean = false;

    constructor(private elementRef: ElementRef) {
    }

    ngAfterViewInit() {
        let currentHeight = this.elementRef.nativeElement.getElementsByTagName('div')[0].offsetHeight;
       //collapsable only if the contents make container exceed the max height
        if (currentHeight > this.maxHeight) {
            this.isCollapsed = true;
            this.isCollapsable = true;
        }
    }
}

答案 3 :(得分:1)

import { Component, Input,OnChanges} from '@angular/core';
@Component({    
    selector: 'read-more',
    template: `
        <div [innerHTML]="currentText"></div>
        <span *ngIf="showToggleButton">
            <a (click)="toggleView()">Read {{isCollapsed? 'more':'less'}}</a>
        </span>`
})

export class ReadMoreDirective implements OnChanges {

    @Input('text') text: string;
    @Input('maxLength') maxLength: number = 100;
    @Input('showToggleButton')showToggleButton:boolean;

    currentText: string;

    public isCollapsed: boolean = true;

    constructor(
        //private elementRef: ElementRef
    ) {

    }
    toggleView() {
        this.isCollapsed = !this.isCollapsed;
        this.determineView();
    }

    determineView() {

        if (this.text.length <= this.maxLength) {
            this.currentText = this.text;
            this.isCollapsed = false;
            return;
        }

        if (this.isCollapsed == true) {
            this.currentText = this.text.substring(0, this.maxLength) + "...";
        } else if(this.isCollapsed == false)  {
            this.currentText = this.text;
        }

    }

    ngOnChanges() {
        if(!this.validateSource(this.text)) {
            //throw 'Source must be a string.';
            console.error('Source must be a string.');
        }
        else{
            this.determineView();
        }
    }

    validateSource(s) {
        if(typeof s !== 'string') {
            return false;
        } else {
            return true;
        }
    }
}

和用法

<read-more [text]="this is test text" [maxLength]="10" [showToggleButton]="true"></read-more>

答案 4 :(得分:0)

如果要在不剪切任何单词的情况下将文本显示为最大字符数,请更改以下代码行:

this.currentText = this.text.substring(0, this.maxLength) + "...";

要:

this.currentText = this.text.substring(0, this.maxLength);
this.currentText = this.currentText.substr(0, Math.min(this.currentText.length, this.currentText.lastIndexOf(" ")))
this.currentText = this.currentText + "..."

答案 5 :(得分:0)

@Andrei Zhytkevich代码仅稍有改进 (对于降价很有用)

import {
  Component,
  AfterViewInit,
  ViewChild,
  ElementRef,
  Attribute,
  ChangeDetectionStrategy } from '@angular/core';

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'ui-read-more',
  template: `
    <div [class.collapsed]="isCollapsed" [style.height]="_height">
      <div #wrapper>
        <ng-content></ng-content>
      </div>
    </div>
    <div class="read-more">
      <button
      type="button"
      class="btn btn-light" (click)="onIsCollapsed()">{{isCollapsed ? 'More' : 'Less'}}</button>
    </div>
  `,
  styles: [`
    :host{
      display: block;
    }
    .collapsed {
      overflow: hidden;
      padding-bottom: 1rem;
    }
    .read-more{
      display: flex;
      justify-content: flex-end;
    }
  `]
})
export class UiReadMoreComponent implements AfterViewInit{
  @ViewChild('wrapper') wrapper: ElementRef;
  isCollapsed: boolean = true;
  private contentHeight: string;
  private _height: string;
  constructor(@Attribute('height') public height: string = '') {
    this._height = height;
  }
  ngAfterViewInit() {
    this.contentHeight = this.wrapper.nativeElement.clientHeight + 'px';
  }
  onIsCollapsed(){
    this.isCollapsed = !this.isCollapsed;
    this._height = this.isCollapsed ? this.height : this.contentHeight;
  }
}

用法

<ui-read-more height="250px">
 <ngx-md>
    {{post.content}}
 </ngx-md>
</ui-read-more>

答案 6 :(得分:0)

谢谢,由于控制台错误,我将其更改为NgOnInit。 略有变化,可以与Angular 6很好地配合。

@Component({
selector: 'app-read-more',
template: `
    <div id="textCollapse" [innerHTML]="text" [class.collapsed]="isCollapsed" [style.height]="isCollapsed ? maxHeight+'px' : 'auto'">
    </div>
        <a *ngIf="isCollapsible" (click)="isCollapsed =! isCollapsed">Read {{isCollapsed ? 'more':'less'}}</a>
`,
styles: [`
    div.collapsed {
        overflow: hidden;
    }

    a {
      color: #007bff !important;
      cursor: pointer;
    }
`]
})
export class ReadMoreComponent implements OnInit {

// the text that need to be put in the container
@Input() text: string;

// maximum height of the container
@Input() maxHeight: number;

// set these to false to get the height of the expended container 
public isCollapsed = false;
public isCollapsible = false;

constructor(private elementRef: ElementRef) {
}

ngOnInit() {
  const currentHeight = document.getElementById('textCollapse').offsetHeight;
  if (currentHeight > this.maxHeight) {
    this.isCollapsed = true;
    this.isCollapsible = true;
  }
 }
}

如您所见,我更改了
const current Height = document.getElementById('textCollapse').offsetHeight;

答案 7 :(得分:0)

我又通过动态数据和完全控制解决了这类问题。

 <div class="Basic-Info-para">
   <p>
     <span *ngIf="personalBasicModel.professionalSummary.length>200" id="dots"> 
         {{personalBasicModel.professionalSummary | slice:0:200}} ...
    </span>
     <span id="more">{{personalBasicModel.professionalSummary }}
</span>
 </p>
</div> 

在此 personalBasicModel.professionalSummary 包含string。像任何文字一样。
         slice:0:20​​0 =将切片管道用于长度为200个字符的接合字符串。您可以根据需要更改长度。 id =“ dots”和id =“ more” 两件重要的事情。

<div class="Basic-Info-SeeMore">
            <button class="SeeMore"(click)="showMore(paasValueOn_SeeMoreBtn)">
                {{showLess_More}}
            </button>
        </div>

在这里,我们定义了一个带有动态文本(点击多看少)并带有点击事件的按钮。

// ---------------------------------- ts文件--------- -------------------------- //

定义变量

showLess_More :字符串=“查看更多...”;
   paasValueOn_SeeMoreBtn :boolean = true;

用户单击“查看更多”按钮时会触发事件(方法)

 showMore(data:boolean){
    if(data){
      $("#dots").css('display', 'none');
      $("#more").css('display', 'inline');
      this.showLess_More = "SEE LESS ...";
      this.paasValueOn_SeeMoreBtn = false;
    }else{
      $("#dots").css('display', 'inline');
      $("#more").css('display', 'none');
      this.showLess_More = "SEE MORE...";
      this.paasValueOn_SeeMoreBtn = true;

    }

  }

答案 8 :(得分:0)

您可以使用此插件。

仅通过传递要默认显示的[text][textLength]非常简单 https://www.npmjs.com/package/nga-read-more

答案 9 :(得分:0)

lineheight方法:-

lineheight很少的计算和一些CSS text-overflow: ellipsis;可以完成这项工作。

.css

.descLess {
  margin-bottom: 10px;
  text-overflow: ellipsis;
  overflow: hidden;
  word-wrap: break-word;
  display: -webkit-box;
  line-height: 1.8;      <==== adjust line-height...a/q to your need
  letter-spacing: normal;
  white-space: normal;
  max-height: 52px;  <==== 250px etc:-
  width: 100%;
  /* autoprefixer: ignore next */
  -webkit-line-clamp: 2; <==== clamp line 2...or 3 or 4 or 5...
  -webkit-box-orient: vertical;
}

.html

    <div class="col-12 rmpm">
          <div id="descLess" *ngIf="seeMoreDesc === 'false'" class="descLess col-12 rmpm">
                {{inputData?.desc}}
           </div>
        <div *ngIf="seeMoreDesc === 'true'" class="col-12 rmpm" style="margin-bottom: 10px;line-height: 1.8;"> 
   <!--Use Line height here-->
                {{inputData?.desc}}
               </div>
        <span class="seeMore" *ngIf="seeMoreDesc === 'false' && lineHeightDesc > 21"
             (click)="updateSeeMore('seeMoreDesc', 'true')">
                 See More
        </span>
        <span class="seeMore" *ngIf="seeMoreDesc === 'true'"
               (click)="updateSeeMore('seeMoreDesc', 'false')">
                   See Less
        </span>
         </div>

.ts

declare const $:any;
seeMoreDesc = 'false';
seeMore = '';
inputData = {
   'desc':'Lorem Ipusme dummy text..................'
 }
 constructor(
        private eRef: ElementRef,
        private cdRef : ChangeDetectorRef
    ) {}
    ngAfterViewChecked() {
       // pass line height here
        this.lineHeightDesc = (Number($('#descLess').height()) / 1.8);
        this.cdRef.detectChanges();
    }


     public updateSeeMore(type, action) {
         if (type === 'seeMoreDesc') {
               this.seeMoreDesc = action;
                this.cdRef.detectChanges();
            } else if (type === 'seeMore') {
                this.seeMore = action;
                this.cdRef.detectChanges();
            }

        }