title: Angular Material Table TypeError: Cannot read property 'cars' of undefined
body: <p>I am having a problem with Angular Material Table (<a href="https://material.angular.io/components/table/overview" rel="nofollow noreferrer">Angular Material Table</a>)</p>
  
  <p>I ran <code>ng generate @angular/material:material-table --name=car-table</code> to generate the default angular table, which works fine. 
  But if I try to inject the data (cars) to the <code>CarsTableDataSource</code> it stops working. It must be something related with async functions and <code>ngOnInit</code> lifecycle hooks.</p>
  
  <p>You can see the code in <a href="https://stackblitz.com/edit/github-wmxs4k" rel="nofollow noreferrer">StackBlitz</a>. The critical part is in the <code>src/app/cars/</code> folder.</p>
  
  <p><strong>cars.component.ts</strong></p>
  
  <pre class="lang-ts prettyprint-override"><code>import {Component, OnInit, ViewChild} from '@angular/core';
  import {Car} from '../car';
  import {CarService} from '../car.service';
  import {MatPaginator, MatSort, MatTable} from '@angular/material';
  import {CarsTableDataSource} from './cars-table-datasource';
  
  @Component({
    selector: 'app-cars',
    templateUrl: './cars.component.html',
    styleUrls: ['./cars.component.css']
  })
  export class CarsComponent implements OnInit {
    cars: Car[];
  
    @ViewChild(MatPaginator) paginator: MatPaginator;
    @ViewChild(MatSort) sort: MatSort;
    @ViewChild(MatTable) table: MatTable&lt;Car&gt;;
    dataSource: CarsTableDataSource;
  
    /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
    displayedColumns = ['id', 'name', 'img_url'];
  
    constructor(private carService: CarService) {
    }
  
    async ngOnInit() {
      console.log('before getting cars: ');
      console.log(this.cars);
      this.cars = await this.carService.getCars().toPromise();
      console.log('got cars:');
      console.log(this.cars);
      this.dataSource = new CarsTableDataSource(this.paginator, this.sort, this.cars);
    }
  
    add(name: string) {
      name = name.trim();
      if (!name) {
        return;
      }
      this.carService.addCar({name} as Car)
        .subscribe(car =&gt; {
          this.cars = [...this.cars, car];
          console.log(this.cars);
          console.log('rendering rows');
          this.table.renderRows();
        });
    }
  
    delete(car: Car) {
      this.cars = this.cars.filter(c =&gt; c !== car);
      this.carService.deleteCar(car).subscribe();
      this.table.renderRows();
    }
  }
  </code></pre>
  
  <p><strong>cars-table-datasource.ts</strong></p>
  
  <pre class="lang-ts prettyprint-override"><code>import {DataSource} from '@angular/cdk/collections';
  import {MatPaginator, MatSort} from '@angular/material';
  import {map} from 'rxjs/operators';
  import {merge, Observable, of as observableOf} from 'rxjs';
  import {Car} from '../car';
  
  /**
   * Data source for the CarsTable view. This class should
   * encapsulate all logic for fetching and manipulating the displayed cars
   * (including sorting, pagination, and filtering).
   */
  export class CarsTableDataSource extends DataSource&lt;CarsTableItem&gt; {
    // cars: CarsTableItem[];
  
    constructor(private paginator: MatPaginator, private sort: MatSort, public cars: Car[]) {
      super();
    }
  
    /**
     * Connect this cars source to the table. The table will only update when
     * the returned stream emits new items.
     * @returns A stream of the items to be rendered.
     */
    connect(): Observable&lt;CarsTableItem[]&gt; {
      // Combine everything that affects the rendered cars into one update
      // stream for the cars-table to consume.
      const dataMutations = [
        observableOf(this.cars),
        this.paginator.page,
        this.sort.sortChange
      ];
  
      // Set the paginator's length
      this.paginator.length = this.cars.length;
  
      return merge(...dataMutations).pipe(map(() =&gt; {
        return this.getPagedData(this.getSortedData([...this.cars]));
      }));
    }
  
    /**
     *  Called when the table is being destroyed. Use this function, to clean up
     * any open connections or free any held resources that were set up during connect.
     */
    disconnect() {
    }
  
    /**
     * Paginate the cars (client-side). If you're using server-side pagination,
     * this would be replaced by requesting the appropriate cars from the server.
     */
    private getPagedData(data: CarsTableItem[]) {
      const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
      return data.splice(startIndex, this.paginator.pageSize);
    }
  
    /**
     * Sort the cars (client-side). If you're using server-side sorting,
     * this would be replaced by requesting the appropriate cars from the server.
     */
    private getSortedData(data: CarsTableItem[]) {
      if (!this.sort.active || this.sort.direction === '') {
        return data;
      }
  
      return data.sort((a, b) =&gt; {
        const isAsc = this.sort.direction === 'asc';
        switch (this.sort.active) {
          case 'name':
            return compare(a.name, b.name, isAsc);
          case 'id':
            return compare(+a.id, +b.id, isAsc);
          default:
            return 0;
        }
      });
    }
  }
  
  /** Simple sort comparator for example ID/Name columns (for client-side sorting). */
  function compare(a, b, isAsc) {
    return (a &lt; b ? -1 : 1) * (isAsc ? 1 : -1);
  }
  </code></pre>
  
  <p><strong>cars.component.html</strong></p>
  
  <pre class="lang-html prettyprint-override"><code>&lt;div&gt;
    &lt;label&gt;Car name:
      &lt;input #carName /&gt;
    &lt;/label&gt;
    &lt;!-- (click) passes input value to add() and then clears the input --&gt;
    &lt;button (click)="add(carName.value); carName.value=''"&gt;
      add
    &lt;/button&gt;
  &lt;/div&gt;
  
  &lt;h2&gt;My Cars&lt;/h2&gt;
  &lt;div class="mat-elevation-z8 centered-table-div"&gt;
    &lt;table mat-table class="full-width-table" [dataSource]="dataSource" matSort aria-label="Elements"&gt;
  
      &lt;!-- Image Column --&gt;
      &lt;ng-container matColumnDef="img_url"&gt;
        &lt;th mat-header-cell *matHeaderCellDef mat-sort-header&gt;Image&lt;/th&gt;
        &lt;td mat-cell *matCellDef="let row"&gt;
          &lt;img [src]="row.img_url" alt="car image" class="car-image"/&gt;
        &lt;/td&gt;
      &lt;/ng-container&gt;
  
      &lt;!-- Id Column --&gt;
      &lt;ng-container matColumnDef="id"&gt;
        &lt;th mat-header-cell *matHeaderCellDef mat-sort-header&gt;Id&lt;/th&gt;
        &lt;td mat-cell *matCellDef="let row"&gt;{{row.id}}&lt;/td&gt;
      &lt;/ng-container&gt;
  
      &lt;!-- Name Column --&gt;
      &lt;ng-container matColumnDef="name"&gt;
        &lt;th mat-header-cell *matHeaderCellDef mat-sort-header&gt;Name&lt;/th&gt;
        &lt;td mat-cell *matCellDef="let row"&gt;{{row.name}}&lt;/td&gt;
      &lt;/ng-container&gt;
  
  
      &lt;tr mat-header-row *matHeaderRowDef="displayedColumns"&gt;&lt;/tr&gt;
      &lt;tr mat-row *matRowDef="let row; columns: displayedColumns;"&gt;&lt;/tr&gt;
    &lt;/table&gt;
  
    &lt;mat-paginator #paginator
                   [length]="dataSource.cars.length"
                   [pageIndex]="0"
                   [pageSize]="5"
                   [pageSizeOptions]="[3, 5, 25, 50]"&gt;
    &lt;/mat-paginator&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>The problem resides in the <code>ngOnInit</code> and</p>
  
  <pre class="lang-html prettyprint-override"><code>  &lt;mat-paginator #paginator
                   [length]="dataSource.cars.length"
                   [pageIndex]="0"
                   [pageSize]="5"
                   [pageSizeOptions]="[3, 5, 25, 50]"&gt;
    &lt;/mat-paginator&gt;
  </code></pre>
  
  <p>As an error I get <code>ERROR TypeError: Cannot read property 'cars' of undefined</code>, which means that <code>dataSource</code> is undefined when parsing the template, but the function <code>ngOnInit</code>: </p>
  
  <pre class="lang-ts prettyprint-override"><code>  async ngOnInit() {
      console.log('before getting cars: ');
      console.log(this.cars);
      this.cars = await this.carService.getCars().toPromise();
      console.log('got cars:');
      console.log(this.cars);
      this.dataSource = new CarsTableDataSource(this.paginator, this.sort, this.cars);
    }
  </code></pre>
  
  <p>prints out:</p>
  
  <p><a href="https://i.stack.imgur.com/cmBdK.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/cmBdK.png" alt="enter image description here"></a>
  <a href="https://i.stack.imgur.com/n3EhQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/n3EhQ.png" alt="enter image description here"></a></p>
  
  <p>The page still loads everything, but I can't for example add cars through the method, because they do add in the database, but don't update in the view despite calling <code>this.table.renderRows()</code> as mentioned in the documentation:</p>
  
  <blockquote>
    <p>Since the table optimizes for performance, it will not automatically check for changes to the data array. Instead, when objects are added, removed, or moved on the data array, you can trigger an update to the table's rendered rows by calling its <code>renderRows()</code> method.</p>
  </blockquote>
  
  <p>I tried making the <code>ngOnInit</code> use <code>Observable</code>s instead of <code>async/await</code>, but it didn't work either: </p>
  
  <pre class="lang-ts prettyprint-override"><code>  ngOnInit() {
      console.log('before getting cars: ');
      console.log(this.cars);
      this.carService.getCars().subscribe(cars =&gt; {
        this.cars = cars;
        console.log('got cars:');
        console.log(this.cars);
        this.dataSource = new CarsTableDataSource(this.paginator, this.sort, this.cars);
      });
    }
  </code></pre>
  
  <p>If I don't do any of the database fetching stuff in the <code>ngOnInit</code>, then there aren't any errors.</p>
  
  <p>I also cannot add any cars with the <code>add()</code> right now as was aforementioned.</p>
  
  <p>If you need any other information - feel free to ask me and I will make sure to answer as soon as I can. </p>
  
  <p><strong>Edit</strong></p>
  
  <p>If I edit the code to look like this:</p>
  
  <pre class="lang-ts prettyprint-override"><code>async ngOnInit() {
    console.log('before getting cars: ');
    console.log(this.cars);
    console.log('got cars:');
    this.cars = await this.carService.getCars().toPromise();
    console.log(this.cars);
    this.dataSource = new CarsTableDataSource(this.paginator, this.sort, this.cars);
  }
  </code></pre>
  
  <p>The order of the errors changes to:</p>
  
  <p><a href="https://i.stack.imgur.com/I6Pwt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/I6Pwt.png" alt="enter image description here"></a>
  <a href="https://i.stack.imgur.com/czOrF.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/czOrF.png" alt="enter image description here"></a></p>
  
  <p>which means that the error happens in the </p>
  
  <pre class="lang-ts prettyprint-override"><code>this.cars = await this.carService.getCars().toPromise();
  </code></pre>
  
  <p>I've already tried it with a <code>.subscribe()</code> and doing everything in that block, but no luck there.</p>
  
  <p><strong>Edit 2</strong></p>
  
  <p>As stated in <a href="https://stackoverflow.com/questions/50022662/angular-material-2-paginator-with-mat-table-initialization?rq=1">here (stackoverflow)</a> you have to initialize the <code>dataSource</code> with a blank object, because the view is parsed before all the microtasks in <code>ngOnInit</code> finish.
  Initialize paginator after view init.</p>
  
  <pre><code>  async ngOnInit() {
      this.dataSource = new CarsTableDataSource(this.paginator, this.sort, []);
      console.log('before getting cars: ');
      console.log(this.cars);
      this.cars = await this.carService.getCars().toPromise();
      console.log('got cars:');
      console.log(this.cars);
      this.dataSource = new CarsTableDataSource(this.paginator, this.sort, this.cars);
    }
  </code></pre>
  
  <p>Now it works, but this is sort of a hack. I don't know why, but whenever where is asyc code in a lifecycle hook in Angular, the hook finishes before the async code has finished. I don't know why. 
  After it sees <code>await</code> it immediately exits the function and only after that does the <code>dataSource</code> get initialized. I would really appreciate an explanation. </p>
  
  <p><strong>Edit 3</strong></p>
  
  <p>Another workaround is adding the null-conditional operator in the view where it breaks like so:</p>
  
  <pre class="lang-html prettyprint-override"><code>  &lt;mat-paginator #paginator
                   [length]="dataSource?.cars.length"
                   [pageIndex]="0"
                   [pageSize]="5"
                   [pageSizeOptions]="[3, 5, 25, 50]"&gt;
    &lt;/mat-paginator&gt;
  </code></pre>
  
  <p>With this line:</p>
  
  <pre><code>[length]="dataSource?.cars.length"
  </code></pre>
  
  <p>As the view is executed when ngOnInit is half-finished, you have to add this everywhere you use that property, so that it doesn't get in the final html when the view is parsed.</p>
  
  <p><strong>Edit 4</strong></p>
  
  <p>I updated the link to the Stackblitz app, it is now as minimalistic as possible to represent the issue.</p>