Search code examples
angularangular6angular7angular-formsangular-ngfor

Iterating Values in Angular 9 from LocalStorage


I have a problem displaying a nested value from Localstorage in Angular. The problem is on the second *ngFor. The first *ngFor display well. The second ngFor has an error of "TypeError: Cannot read property 'toUpperCase' of undefined"? I don't know why the is toUppercase since i didn't use uppercase here

PIC

HTML

<mat-list-item role="listitem" *ngFor="let bookmark of bookmarks">
     {{ extractNameFromJson(bookmark[1]).id }}
         <ul>
           <li *ngFor="let extractNameFromJson(bookmark[1])?.cuesData of bookmark">
                   {{ }}   
            </li>
         </ul>
 </mat-list-item>

TS

 getAllBookmarks() {
   this.bookmarks = Object.entries(localStorage);
   console.log(this.bookmarks);
 }

 extractNameFromJson(obj) {
    obj = JSON.parse(obj);
    return obj;
  }

Solution

  • You need to get the value of each bookMark as a variable in *ngFor first. You cannot have a function call while extracting a variable from a loop using ngFor. One way can be to have a pipe which does this filtering for you while looping, or another way could be to use ng-container to loop and only show lis if your condtion passes.

    <ng-container *ngFor="let eachBookMark of bookmark">
        <ng-container *ngIf="extractNameFromJson(eachBookMark[1]) as data">
            <ng-container *ngIf="data.cuesData">
                <li *ngFor="data.cuesData">
                    {{data | json}}
                </li>
            </ng-container>
        </ng-container>
    </ng-container>
    

    EDIT: if you want to change the bookmarks array in your ts, maybe you can do this:

    this.bookmarks = this.bookmarks.map(([first, second]) => {
        return [first, JSON.parse(second)];
    });
    

    Now, if you have done this, then your HTML can be simply:

    <mat-list-item role="listitem" *ngFor="let eachBookMark of bookmarks">
        <ul>
            <ng-container *ngIf="eachBookMark[1].cuesData as cuesData">
                <li *ngFor="let eachCue of cuesData">
                    {{ eachCue.description }}   
                </li>
            </ng-container>
        </ul>
    </mat-list-item>
    

    EDIT2:

    this.filteredBookmarks = this.bookmarks.reduce((acc, [first, second]) => {
        const parsedOb = JSON.parse(second);
        if (parsedOb.cuesData) {
            acc.push([first, parsedOb]);
        }
        return acc;
    }, []);
    
    <mat-list-item role="listitem" *ngFor="let eachBookMark of filteredBookmarks">
        <ul>
            <li *ngFor="let eachCue of cuesData">
                    {{ eachCue.description }}   
            </li>
        </ul>
    </mat-list-item>
    

    All the above filtering, (map/reduce) would happen at the place where you are populating bookmarks.