0

I am new to Angular and I am writing a custom Component as mentioned below. I am displaying a list of Course(object) that has 2 properties, id and name. This logic works fine. However when I am writing a logic to add new Course in the existing array of Course in custom-component.html using ngModel for binding, I am getting following error:

ERROR TypeError: Cannot read property 'name' of undefined.

My code is given below:

custom-component.component.html

<h2>{{ title }}</h2>
<ul class="courses">
  <li *ngFor="let course of courses" (click)="onSelect(course)"
  [class.selected]="course===selectedCourse">
      <span class="badge">{{course.id}}</span> {{course.name}}
  </li>
</ul>
<div *ngIf="selectedCourse">
  <ul class="courses"><li>
  <span class="badge">{{selectedCourse.id}}</span> {{selectedCourse.name}}</li>
</ul>
</div>
<div>
<span>Enter name: </span><input type="text" name="name" [(ngModel)]="course.name">
<span>Enter id:</span><input type="text" name="id" [(ngModel)]="course.id">
<button (click)="addCourse(course)">Add Course</button>
</div>

custom-component.component.ts

import { Course } from './../Course';
import { Component, OnInit } from '@angular/core';
import { CoursesService } from '../courses.service';

@Component({
  selector: 'app-custom-component',
  templateUrl: './custom-component.component.html',
  styleUrls: ['./custom-component.component.css']
})
export class CustomComponent implements OnInit {
  title = "Choosen Courses";
  selectedCourse: Course;
  courses: Course[];
  constructor(service: CoursesService) {
    this.courses = service.getCourse();
   }

  ngOnInit() {
  }
  onSelect(course: Course):void{
    this.selectedCourse=course;
  }
  addCourse(course: Course):void{
    this.courses.push(course);
  }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { CustomComponent } from './custom-component/custom-component.component';
import { CoursesService } from './courses.service';


@NgModule({
  declarations: [
    AppComponent,
    CustomComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [
    CoursesService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
halfer
  • 19,824
  • 17
  • 99
  • 186
Sachin Kumar
  • 169
  • 2
  • 8
  • Please don't ask for upvotes on Stack Overflow. We want votes to be as natural as possible here, and some people may even downvote you for asking. In my experience, _answering_ questions is a more reliable way to obtain rep points. – halfer Jan 31 '18 at 15:26

4 Answers4

4

Based on comment

Initialize a new property

course: Course = new Course().

Then in the addCourse you can pass nothing and use that object directly. After push, change the reference to keep it separately from the pushed object.

addCourse(): void {
   this.courses.push(this.course);
   this.course = new Course();
}

This is another case, the upper one will work for you

When you do this

<button (click)="addCourse(course)">Add Course</button>

and click the button, you don't have a property with name course, so undefined is passed to the function and added into the array. After this when *ngFor is going to update the views, the last added element is undefined. And trying to access it's properties will throw error.

Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112
1

Error detail:

If you check a condition for *ngIf="course" then the error should go. because you are not initialize course object in your component. try this for fix the error

<span *ngIf="course">Enter name: </span><input type="text" name="name" [(ngModel)]="course.name">

Proper Solution

You should initialize your object before use it in html

addCourses: Course = new Course();

and your html should be

<span *ngIf="course">Enter name: </span><input type="text" name="name" [(ngModel)]="addCourses.name">
<span>Enter id:</span><input type="text" name="id" [(ngModel)]="addCourses.id">
<button (click)="addCourse(addCourses)">Add Course</button>
Ramesh Rajendran
  • 37,412
  • 45
  • 153
  • 234
0

[(ngModel)]="course.name" You should bind property to an initialized object, so try to declare it in component:

private course: Course = new Course(...);

Sh. Pavel
  • 1,584
  • 15
  • 28
0

You are getting error because of this block :

Issue :

For adding data there is no need of 2 way data binding , you should use it when you need to updated something , for update you can simply use form

Reason of error is [(ngModel)]="course.name" and [(ngModel)]="course.id" , as there no value to bind with input

<div>
    <span>Enter name: </span><input type="text" name="name" [(ngModel)]="course.name">
    <span>Enter id:</span><input type="text" name="id" [(ngModel)]="course.id">
    <button (click)="addCourse(course)">Add Course</button>
</div>

It should look like this :

<form #cource='ngForm' (submit)='addCourse(cource.value)'>
    <span>Enter name: </span><input type="text" name="name" ngModel>
    <span>Enter id:</span><input type="text" name="id" ngModel>
    <button type="submit">Add Course</button>
</form>
Vivek Doshi
  • 56,649
  • 12
  • 110
  • 122