Search code examples
angularangular-templateangular17

new template syntax: if(first) inside for is not working


I want to use the new @for syntax together with @empty to either show the user a table or some text telling there is no data.

With ngFor I usually checked the length of the data array. If not empty:

  1. Add the table header
  2. ngFor the data
  3. Add the table footer

With the newer syntax I hoped to be able to combine those 3 steps above into the @for itself like this:

@for(order of licenseOverview.orders; track order.id; let firstRow = $first; let lastRow = $last) {
   @if(firstRow) {
       <table ...
   }

   <tr>
       <td>{{ order.reference }}</td>
       <td>{{ ... }}</td>
   </tr>

   @if(lastRow) {
       ... </table>
   }
}
@empty { <p>No data for you!</p> }

I expected this to just compile and render the table, but it seems like Angular can't handle this. Is there a way to get this to work?

EDIT: The error I get looks like this:

            @if(firstRow) {
                <table class="table table-responsive ds-table">
                    <thead>
                        <tr>
                            <th>{{ 'Order' }}</th>
                            <th>{{ 'Expected On' }}</th>
                            <th>{{ 'Status' }}</th>
                        </tr>
                    </thead>
                    <tbody>
            } ---> Unexpected closing block

Solution

  • We can use @empty to return just a single row along with a td tag with the value "No Results Found", working example below!

    Code is almost the same, but the table must follow a particular syntax, where the table is at the top of loop, then followed by tbody(optional), then we should use the @for to loop through the rows one by one and add them, when no results found, we add a single row with a td with no results found. We can use CSS to make the table more presentable, but overall this is the approach to take!

    Also due to the misformed table structure the for loop and $first didn't work, else it works great!

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
      <table>
        <tbody>
          @for(order of licenseOverview.orders; track order.id; let firstRow = $first;
          let lastRow = $last) { 
            @if(firstRow) {
              <tr>
                <td>Reference</td>
                <td>Something</td>
              </tr>
            }
            <tr>
              <td>{{ order.reference }}</td>
              <td>{{ '...' }}</td>
            </tr>
            @if(lastRow) {
            <tr>
              <td>Reference</td>
              <td>Something</td>
            </tr>
            } 
          } @empty {
            <tr>
              <td>No Results Found!</td>
            </tr>
          }
        </tbody>
      </table>
      `,
    })
    export class App {
      licenseOverview: any = {
        orders: [
          { id: 1, reference: 'test' },
          { id: 2, reference: 'test' },
          { id: 3, reference: 'test' },
          { id: 4, reference: 'test' },
          { id: 5, reference: 'test' },
        ],
      };
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo