Search code examples
angularstring-interpolation

How can I show only the first n words from a string variable showed in my Angular page?


I am working on an Angular application and I have the following problem.

Into my view I am iterating on a list of object rendering them into a "table". In this table I have a column containing textual values that can be very long and I need to show only the first 10 words but it is not working.

I have tried in this way:

<div class="ion-padding-start ion-padding">
  <div class="ion-text-center">
    <ng-container>
      <ion-text>
        <h3>
          TEST
        </h3>
        </ion-text>

        <ion-list>
          <ion-grid>
            <ion-row *ngFor="let bid of artistAppliedBidList">
                    <ion-col>
                      <ion-thumbnail>
                        <img [src]="bid.imageUrl">
                      </ion-thumbnail>
                    </ion-col>
                    <ion-col>{{ bid.bidOwnerName }}</ion-col>
                    <ion-col>{{ bid.vicinity }}</ion-col>
                    <ion-col>{{ bid.description.split(" ").splice(0,10).join(" "); }}</ion-col>
                          
              </ion-row>
         
        </ion-grid>
        </ion-list>
    </ng-container>
  </div>
</div>

But this line:

<ion-col>{{ bid.description.split(" ").splice(0,10).join(" "); }}</ion-col>

give me this error message into the Chrome console:

ERROR Error: Uncaught (in promise): Error: Template parse errors:
Parser Error: Binding expression cannot contain chained expression at the end of the expression [{{ bid.description.split(" ").splice(0,10).join(" "); }}] in ng:///ProfileArtistAppliedBidComponent/template.html@19:29 ("}}</ion-col>
                    <ion-col>{{ bid.vicinity }}</ion-col>
                    <ion-col>[ERROR ->]{{ bid.description.split(" ").splice(0,10).join(" "); }}</ion-col>
                          
       "): ng:///ProfileArtistAppliedBidComponent/template.html@19:29
Error: Template parse errors:
Parser Error: Binding expression cannot contain chained expression at the end of the expression [{{ bid.description.split(" ").splice(0,10).join(" "); }}] in ng:///ProfileArtistAppliedBidComponent/template.html@19:29 ("}}</ion-col>
                    <ion-col>{{ bid.vicinity }}</ion-col>
                    <ion-col>[ERROR ->]{{ bid.description.split(" ").splice(0,10).join(" "); }}</ion-col>
                          
       "): ng:///ProfileArtistAppliedBidComponent/template.html@19:29
    at syntaxError (compiler.js:2387)
    at htmlAstToRender3Ast (compiler.js:15299)
    at parseTemplate (compiler.js:18076)
    at CompilerFacadeImpl.compileComponent (compiler.js:18932)
    at Function.get (core.js:39843)
    at getComponentDef (core.js:2170)
    at core.js:39571
    at Array.forEach (<anonymous>)
    at setScopeOnDeclaredComponents (core.js:39561)
    at flushModuleScopingQueueAsMuchAsPossible (core.js:39143)
    at resolvePromise (zone-evergreen.js:798)
    at resolvePromise (zone-evergreen.js:750)
    at zone-evergreen.js:860
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:41645)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)
    at drainMicroTaskQueue (zone-evergreen.js:569)

I was able to show only the firts 10 characters replacing the previous expression with;

bid.description.substring(0,8);

but in this way I will have that the last word is truncated and I don't won't it.

How can I implement the desired behavior? How to correctly show only the first 10 words?


Solution

    1. Avoid attempting to run functions in property bindings and interpolations. In case of default change detection strategy, they'll be run for every CD cycle and might affect the performance.

    2. You could use the Angular slice pipe instead.

    <ion-col>{{ bid.description | slice:0:10 }}...</ion-col>
    

    Update: get first 'n' words

    You could write a quick pipe that contains your string manipulation to return the specific number of words instead.

    Additionally you could also use safe navigation operator ?. to make sure the property is defined before trying to invoke functions on it.

    slice-words.pipe.ts

    import { Pipe, PipeTransform } from "@angular/core";
    
    @Pipe({ name: "sliceWords" })
    export class SliceWordsPipe implements PipeTransform {
      transform(value: string, start: number, end?: number): string {
        if (value == null) return null;
    
        return value
          .split(" ")
          .splice(start, end)
          .join(" ");
      }
    }
    

    Template

    <ion-col>{{ bid?.description | sliceWords:0:10 }}...</ion-col>
    

    Working example: Stackblitz