Je dois construire une directive readmore dans Angular2. Ce que cette directive va faire est pour réduire et développer de longs blocs de texte avec les liens "Lire plus" et "Fermer". Pas sur la base du nombre de caractères mais sur la base de la hauteur maximale spécifiée.
<div read-more [maxHeight]="250px" [innerHTML]="item.details">
</div>
Quelqu'un peut-il indiquer la méthode la plus fiable pour obtenir/définir la hauteur de l'élément pour ce cas particulier?.
Toute directive sur la manière dont cette directive spécifique pourrait être mise en œuvre serait également très appréciée.
J'ai besoin de construire quelque chose comme ceci https://github.com/jedfoster/Readmore.js
Solution:
Avec l’aide d’Andzhik, je peux construire le composant ci-dessous qui répond à mes besoins.
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>
S'il y a des améliorations, n'hésitez pas à suggérer.
Je pense que vous aurez besoin d'une Component
plutôt que Directive
. Components
est plus logique car vous devez ajouter En savoir plus bouton/lien, c'est-à-dire mettre à jour 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;
}
Usage:
<read-more>
<!-- you HTML goes here -->
</read-more>
J'ai créé une version qui utilise la longueur du caractère plutôt que la taille de 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();
}
}
Usage:
<read-more [text]="text" [maxLength]="100"></read-more>
Avec l’aide d’Andzhik, je peux construire le composant ci-dessous qui répond à mes besoins.
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>
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;
}
}
}
et utilisation
<read-more [text]="this is test text" [maxLength]="10" [showToggleButton]="true"></read-more>
Si vous souhaitez afficher le texte avec le nombre maximal de caractères sans couper de Word, changez cette ligne de code:
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 + "..."
Encore une fois, j'ai résolu ce type de problème avec des données dynamiques et un contrôle total.
<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>
Ici, personalBasicModel.professionalSummary contient une chaîne. comme n'importe quel texte.
slice: 0: 200 _ = utilise un tube de slice pour une chaîne d'épissure d'une longueur de 200 caractères. vous pouvez modifier la longueur en fonction de vos besoins .id = "points" & id = "plus" deux choses importantes.
<div class="Basic-Info-SeeMore">
<button class="SeeMore"(click)="showMore(paasValueOn_SeeMoreBtn)">
{{showLess_More}}
</button>
</div>
Ici, nous définissons un bouton avec texte dynamique) (voir plus et moins) avec événement de clic.
// ---------------------------------- ts fichier ------------ ----------------------- //
Définir la variable
showLess_More: string = "VOIR PLUS ...";
paasValueOn_SeeMoreBtn: boolean = true;
L'événement (méthode) se déclenche lorsque l'utilisateur clique sur le bouton voir plus
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;
}
}
Juste une légère amélioration du code @Andrei Zhytkevich (Utile pour démarquer)
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>