Angular 2 Read More Directive

2020-01-28 05:44发布

I need to build a readmore directive in Angular2. What this directive will do is for collapse and expand long blocks of text with "Read more" and "Close" links. Not on the basis of the character count but on the basis of the specified max height.

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

Can anyone please guide what would be the most reliable way to get/set the height of the element for this specific case.

Any guidelines on how this specific directive could be implemented would also be highly appreciated.

I need to build something like this https://github.com/jedfoster/Readmore.js

Solution:

With the help from Andzhik I am able to build the below component that meets my requirements.

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

Usage:

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

If there could be any improvements, please feel free to suggest.

10条回答
乱世女痞
2楼-- · 2020-01-28 06:09

You can use this plugin.

It is very simple to use only to pass [text] and [textLength] which you want to show by default https://www.npmjs.com/package/nga-read-more

查看更多
Anthone
3楼-- · 2020-01-28 06:13

Just a slight improvement of @Andrei Zhytkevich code (useful for markdown)

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

Usage

<ui-read-more height="250px">
 <ngx-md>
    {{post.content}}
 </ngx-md>
</ui-read-more>
查看更多
【Aperson】
4楼-- · 2020-01-28 06:15

Thank you, I changed it up a bit for it be on NgOnInit because of the console error. There's a slight change to it and works well with 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;
  }
 }
}

As you can see I changed the
const current Height = document.getElementById('textCollapse').offsetHeight;

查看更多
成全新的幸福
5楼-- · 2020-01-28 06:16

I made a version that uses character length rather than div size.

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();       
    }
}

Usage:

<read-more [text]="text" [maxLength]="100"></read-more>
查看更多
登录 后发表回答