【问题标题】:Angular 2 Read More DirectiveAngular 2 阅读更多指令
【发布时间】:2016-10-15 14:48:19
【问题描述】:

我需要在 Angular2 中构建一个 readmore 指令。该指令将使用“阅读更多”和“关闭”链接折叠和展开长文本块。不是基于字符数,而是基于指定的最大高度。

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

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

【问题讨论】:

标签: angular typescript angular2-template angular2-directives


【解决方案1】:

我制作了一个使用字符长度而不是 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>

【讨论】:

  • 谢谢,这正是我所需要的!
  • 带有html标签的内容呢?
  • 我认为它应该适用于 html。它使用 [innerHTML] 指令
  • @DavidBracoly stackoverflow 旨在回答问题,而不是响应 github 之类的功能请求。如果您想添加这些功能,那么实现您想要的更改并不难。也就是说,如果你想添加这些功能,我给你我的贝宝账户,你可以转给我 200 美元,我会看看我能做什么:)
  • 模板中的小改动...添加 *ngIf 到锚点...*ngIf="text.length >= this.maxLength"
【解决方案2】:

我认为您需要Component 而不是Directive。 Components 更有意义,因为您需要添加 Read more 按钮/链接,即更新 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;
}

用法:

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

【讨论】:

  • 非常感谢 Andzhik 的回复。我对组件进行了一些更改:模板:`
    阅读更多 `styles: [` div.collapsed { height: 150px;溢出:隐藏; } `] 我还添加了@Input() text: string;但我仍然需要一种方法来获取包含文本的 div 的高度,以便在此基础上我可以决定是显示还是隐藏阅读更多链接。
  • 您可以使用ElementRef ngcourse.rangle.io/handout/advanced-components/elementref.html 直接访问代表您的Component 的DOM 元素。 PS:如果您对我原来的回答满意,请采纳,谢谢。
  • 我试过了,但是当我在构造函数或 ngAfterContentChecked 中登录控制台时,它返回 undefined console.log(this.el.nativeElement.height);我也试过 @HostBinding('style.height') private height: string;但它也未定义。
  • 看起来更好的生命周期钩子是ngAfterViewInit,你需要得到this.el.nativeElement.offsetHeight而不是...height。 height 是 `this.el.nativeElement.style' 的成员,此时没有任何价值。
  • 非常感谢 Andzhik,你非常有帮助,我已经在下面发布了我的最终阅读更多组件,如果有任何改进,请提出建议。
【解决方案3】:

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

【讨论】:

    【解决方案4】:
    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;
            }
        }
    }
    

    和用法

    &lt;read-more [text]="this is test text" [maxLength]="10" [showToggleButton]="true"&gt;&lt;/read-more&gt;

    【讨论】:

    • 没有 CSS 的最佳答案
    【解决方案5】:

    我再次用动态数据和完全控制解决了这些类型的问题。

     <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 包含字符串。像任何文本一样。
    slice:0:200 = 对长度为 200 个字符的拼接字符串使用切片管道。你可以根据你的要求改变长度。 id="dots" & id="more" 两个重要的东西。

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

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

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

    定义变量

    showLess_More : string = "SEE 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;
    
        }
    
      }
    

    【讨论】:

      【解决方案6】:

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

      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 + "..."
      

      【讨论】:

        【解决方案7】:

        只是对@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>
        

        【讨论】:

          【解决方案8】:

          谢谢,由于控制台错误,我在 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;

          【讨论】:

            【解决方案9】:

            你可以使用这个插件。

            很简单,只需要传递[text]和[textLength]就可以默认显示 https://www.npmjs.com/package/nga-read-more

            【讨论】:

              【解决方案10】:

              线高方法:-

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

              【讨论】:

                猜你喜欢
                相关资源
                最近更新 更多
                热门标签