Derked
Derked

Reputation: 994

Angular 2 two way data binding with ngModel

First off I am new to Typescript and Angular 2 and this seems like an obvious answer but I can't seem to get it to work. I have the following model

export interface IBrand {
    brandID: number;
    name: string;
    image: string;
}

Then I have the component class

import { Component, OnInit }  from '@angular/core';
import { Router, RouteParams } from '@angular/router-deprecated'
import { bootstrap } from '@angular/platform-browser-dynamic';

import { IBrand } from './brand';
import { BrandService } from './brand.service';

@Component({
    selector: 'data-bind',
    templateUrl: 'app/dashboardApp/brands/brand-list.component.html',
    styleUrls: ['app/dashboardApp/brands/brand-list.component.css']
})

export class BrandListComponent implements OnInit {
    brands: IBrand[];
    errorMessage: string;
    newBrand: IBrand;
    pageTitle: string = 'Brands';

    constructor(private _brandService: BrandService,
        private _router: Router) {
    }

    ngOnInit(): void {
        this._brandService.getBrands()
            .subscribe(
            brands => this.brands = brands,
            error => this.errorMessage = <any>error);
    }    
}

And then I have the following html

<div class="form-group">
    <label for="brandName">Brand:</label>
    <input type="text" class="form-control" placeholder="Name" id="brandName" [(ngModel)]="newBrand.name" />
</div>

I can't get the properties of IBrand to work successfully and I get the error

platform-browser.umd.js:962 ORIGINAL EXCEPTION: TypeError: Cannot read property 'name' of undefined

However I can bind it easily to something like pageTitle. Any ideas that can point me in the right direction?

Upvotes: 5

Views: 7490

Answers (5)

JHM
JHM

Reputation: 411

You probably found out already, but you can/should use: newBrand: IBrand = {}; if your interface properties are optional:

export interface IBrand {
    brandID?: number;
    name?: string;
    image?: string;
}

Upvotes: 0

Gunjan Khanwilkar
Gunjan Khanwilkar

Reputation: 2495

You could follow this approach if you don't want to use class.

export interface IBrand {
brandID: number;
name: string;
image: string;
}

And now you could create empty objects for typed variables like this.

brand: IBrand = {} as IBrand;

Avoid using classes if you just want to use it for type safety instead use interface as best practice to make your bundle size smaller.

Upvotes: 6

Derked
Derked

Reputation: 994

It's been a while since I have asked this question, and it's starting to get some views so I will add my answer.

First I would need to change my interface to a class and add a constructor.

export class Brand {
  constructor() {}
  brandID: number;
  name: string;
  image: string;
}

Now that I have a constructor I can use the new operator to instantiate the object.

export class BrandListComponent implements OnInit {
  brands: Brand[];
  errorMessage: string;
  newBrand: Brand = new Brand();
  pageTitle: string = 'Brands';

  (...)
}

Now I have the desired brand initialized without any data and I can bind it to the model. Hope this helps.

Upvotes: 9

İsmail Şener
İsmail Şener

Reputation: 423

You have to write a class for types. Not Interfaces.

newBrand: Brand = new Brand();

Upvotes: -1

Thierry Templier
Thierry Templier

Reputation: 202138

I think that you need to initialize your newBrand property as described below:

export class BrandListComponent implements OnInit {
  brands: IBrand[];
  errorMessage: string;
  newBrand: IBrand = {}; // <-----
  pageTitle: string = 'Brands';

  (...)
}

In your case, this property is never initialized and you have the error:

Cannot read property 'name' of undefined

Upvotes: 1

Related Questions