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 t
I think you'll need a Component
rather then Directive
. Components
makes more sense since you need to add Read more button/link, i.e. update DOM.
@Component({
selector: 'read-more',
template: `
<div [class.collapsed]="isCollapsed">
<ng-content></ng-content>
</div>
<div (click)="isCollapsed = !isCollapsed">Read more</div>
`,
styles: [`
div.collapsed {
height: 250px;
overflow: hidden;
}
`]
})
export class ReadMoreComponent {
isCollapsed = true;
}
Usage:
<read-more>
<!-- you HTML goes here -->
</read-more>
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;
}
}
<ui-read-more height="250px">
<ngx-md>
{{post.content}}
</ngx-md>
</ui-read-more>
Again i solved these types of problem with dynamic data and full controlling.
<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>
Here , personalBasicModel.professionalSummary contain string . like any text.
slice:0:200 = use slice pipe for splice string with 200 character length . you can be change length according your requirement.
id="dots" & id="more" two important thing.
<div class="Basic-Info-SeeMore">
<button class="SeeMore"(click)="showMore(paasValueOn_SeeMoreBtn)">
{{showLess_More}}
</button>
</div>
Here we define a button with dynamic text (see more and see less ) with click event.
//---------------------------------- ts file -----------------------------------//
Define variable
showLess_More : string = "SEE MORE...";
paasValueOn_SeeMoreBtn : boolean = true;
Event(Method) fire when user click on see more button
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;
}
}
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;
}
}
}
and usage
<read-more [text]="this is test text" [maxLength]="10" [showToggleButton]="true"></read-more>
If you want to display the text to the maximum number of chars without cutting any word, change this line of code:
this.currentText = this.text.substring(0, this.maxLength) + "...";
To:
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 + "..."
lineheight
and little calculation and some css text-overflow: ellipsis;
does this job.
.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;
}
<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>
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();
}
}