Search code examples
javascriptangulartypescriptangular2-http

TypeError: Cannot read property 'Id' of undefined on edit view template - Angular 2


Okay so I have been facing the dreadful:

TypeError: Cannot read property 'Id' of undefined

Before we get started:

@angular/cli: 1.4.4 node: 6.10.3 npm: 3.10.10

Just to give more context, I am trying to perform one way data binding to edit a component by taking the Id from its component class and flow in a single direction to display the view template. That's all.

Below is the following that will hopefully try reproduce the problem and in turn figure out a solution.

  1. SQL Table Definition:
CREATE TABLE [ExampleTable]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Col2] [nvarchar](50) NULL,
    [Col3] [int] NULL,
    [Col4] [int] NULL
)
  1. ExampleTable.ts

export interface ExampleTable {
    Id;
    Col2;
    Col3;
    Col4;
}

export class CreateExampleTableModel {
    SomeForeignKey?: number;
    Col2: string;
    Col2: number; 
    Col2: number; 
}

export class EditExampleTable {

}

  1. empty-tables.component.ts

import {
  Component
} from '@angular/core';
import {
  Router
} from "@angular/router";
import {
  EmptyTableServiceService
} from "../../services/empty-table.service";
import {
  EmptyTable
} from "../../models/outModels/EmptyTable";


@Component({
  selector: 'app-empty-tables',
  templateUrl: './empty-tables.component.html',
  styleUrls: ['./empty-tables.component.css']
})
export class EmptyTablesComponent {
  //Table data
  emptyTable: EmptyTable[];


  constructor(
    private router: Router,
    private emptyTableServiceService: EmptyTableServiceService) {

  }

  edit(emptyTable: EmptyTable) {
    this.router.navigate(['emptyTables/edit', emptyTable.Id]);
  }
}

  1. EmptyTableService:

import {
  Injectable
} from '@angular/core';
import {
  Http
} from '@angular/http';
import 'rxjs/add/operator/toPromise';
import {
  EmptyTable,
  CreateExampleTableModel
} from "../models/outModels/EmptyTable";

@Injectable()
export class EmptyTableService {
  constructor(private http: Http, ) {}

getEmptyTable(Id: string): Promise<EmptyTable> {
    return this.http.get(`${this.auth.apiUrl}/api/emptyTables/get/${Id}`, { headers: this.auth.header })
        .toPromise()
        .then(response => response.json() as EmptyTable)
        .catch(error => this.logging.handleError(error));
}

  update(emptyTable: EmptyTable): Promise < EmptyTable > {
    return this.http.post(`${this.auth.apiUrl}/api/emptyTables/update`, JSON.stringify(emptyTable), {
        headers: this.auth.header
      })
      .toPromise()
      .then(response => response.json() as EmptyTable)
      .catch(error => this.logging.handleError(error));
  }
}

  1. EmptyTableEditComponent:

import {
  Component,
  OnInit
} from '@angular/core';
import {
  ActivatedRoute,
  ParamMap,
  Router
} from '@angular/router';
import {
  EmptyTableService
} from "../../../services/empty-table.service";
import {
  EmptyTable
} from "../../../models/outModels/EmptyTable";

export class EmptyTableEditComponent implements OnInit {

  model: EmptyTable;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private emptyTableService: EmptyTableService
  ) {}

  ngOnInit() {
    this.loading = true;
    this.route.paramMap
      .switchMap((params: ParamMap) => this.emptyTableService.getEmptyTable(params.get('Id')))
      .subscribe(emptyTable => {
        this.model = emptyTable;
      });
  }

  goBack(): void {
    this.router.navigate(['/emptyTables']);
  }

  save(): void {
    this.loading = true;
    this.emptyTableService.update(this.model).then(
      emptyTable => {
        this.model = emptyTable;
      },
      error => {
        console.log(error);
      }
    );
  }
}

My suspicion is that in my getEmptyTable(Id: string) which returns a Promise of EmptyTables is that I am passing in my Id parameter as a string value whereas in my table definition from my DB it is an integer however according to my understanding, url parameters are always in string format. I tried the following:

i. Setting my Id to a number data type and I call the toString() on the Idparameter in the apiUrl like so:

 getEmptyTable(Id: number): Promise<EmptyTable> {
        return this.http.get(`${this.auth.apiUrl}/api/emptyTables/get/${Id.toString()}`, { headers: this.auth.header })
            .toPromise()
            .then(response => response.json() as EmptyTable)
            .catch(error => this.logging.handleError(error));
    }

But this does not make much of a difference. Lastly, please find the view template which I render:

<div class="container">
  <p-messages [(value)]="messages"></p-messages>
  <p-panel *ngIf="model">
    <p-header>
      Edit EmptyTable {{model.Name}}
    </p-header>
    <form name="form" (ngSubmit)="save()">

      <div class="form-group">
        <label>Col 2</label>
        <input type="text" class="form-control" name="col2" [(ngModel)]="model.Col2" required />
      </div>

      <div class="form-group">
        <label>Col 3</label>
        <input type="text" class="form-control" name="col3" [(ngModel)]="model.Col3" required />
      </div>

      <div class="form-group">
        <button pButton type="button" class="ui-button-secondary" (click)="goBack()" label="Back" icon="fa-chevron-left"></button>
        <button pButton class="ui-button-success pull-right" label="Save" icon="fa-save"></button>
        <app-loader *ngIf="loading"></app-loader>
      </div>
    </form>
  </p-panel>
</div>

To wrap this up, it complains in the following function:

  edit(emptyTable: EmptyTable) {
        this.router.navigate(['emptyTables/edit', emptyTable.Id]);
    }

Note: Please don't run the snippets as there is no output to them. This was the quickest way to format my code. Manual indentation was not cutting it.


Solution

  • The problem was found below:

     <ng-template let-user="rowData" pTemplate="body">
                        <button type="button" pButton (click)="edit(distributor)" icon="fa-edit"></button>
                    </ng-template>
    

    let-user should have been changed to let-distributor and all works.