3

I'm having some trouble displaying my data in the browser. To explain my problem I'm using some dummy code. I have some nested objects that are causing my problem. Here I'll display one nested object to showcase my problem.

First of all, I only make http calls for the Car-object. So saveCar acts like updating the car as well, depending on what the user does in the app. All the methods in the service works as they should.

So my service looks something like this:

@Injectable()
export class Service {

    constructor(private http: Http) { }

    saveCar(car: Car) {
       return this.http.post ....      
    }

    getCars(){
        return this.http.get...       
    }

    getById(id: string) {
        return this.http.get...       
    }    
}

Then I have a Car-class, where the nested object "Brand" comes in to play, Brand then has it's own class, but I'll leave it out.

export class Car {

    private brands: Array<Brand>;

    constructor(public id: string, public name: string) {
        this.brands = new Array<Brand>();
    }

    public getBrands(): Array<Brand> {
        return this.brands;
    }

    public addBrand(value: Brand): void {
        this.brands.push(value);
    }
    //some other methods.
}

Then I have a list-component that lists all cars, this works as it should!

@Component({
    selector: 'car-list',
    template: `
    <h1>Add Car</h1>
    <form (submit)="saveCar()">
        <input required [(ngModel)]="name" placeholder="Add car">
    </form>
    <br>
    <table>
        <tr *ngFor="let car of cars" >
        <td>{{car.name}}</td>
        <td><button (click)="goToDetail(car)">Detail</button></td>
        </tr>
    </table>
  `,
})


export class ListComponent implements OnActivate {
    id: string
    name: string;
    cars: Array<Car>

    constructor(public _service: Service, public _router: Router) {   }

    routerOnActivate(): void {
        this._service.getCars()
            .subscribe(cars => this.cars = cars);
    }

    saveCar() {
        let car = new Car(this.id, this.name)

        this._service.saveCar(Car)
            .subscribe(car => this.cars.push(car));

        this._service.getCars()//
            .subscribe(cars => this.cars = cars);
    }

    goToDetail(car:Car) {
        this._router.navigate(['/cardetail', car.id]);
    }

}

The problem I have is in the detail-component, where the user gets navigated after clicking a specific car. The routing and retrieving the Car from the db works as it should. That I know, because if I remove all the template except <h1>Car: {{car?.name}}</h1> the name gets printed out fine with the elvis operator.

But my detail-component looks something like this:

@Component({
    selector: 'car-detail',
    template: `  
    <h1>Car: {{car?.name}}</h1>
    <hr>
    <button (click)="addBrand()">Add Brand</button>      
    <div *ngFor="let brand of car.getBrands(); let i=index">  
    <h2>Brand {{i+1}}</h2>
    </div>               
`,
})

export class DetailComponent implements OnActivate {

    @Input() car: Car;

    constructor(public _service: Service, public _router: Router) {    }

    routerOnActivate(curr: RouteSegment): void {
        let id = curr.getParam('id');
        this._service.getById(id)
            .subscribe(car => {
                this.car = car;
            });
    }

    addBrand() {
        this.car.getBrands().push(new Brand());
    }

    //some other methods
}

So in my detail component I call all methods like: car.someMethod() and further on the nested Brand object like: brand.someMethod() in the template. So the error comes at the call of the method e.g in the template 'cannot get getBrands of undefined' I've tried putting the elvis operator like this: car?.getBrands() It doesn't work. I've tried to wrap the whole thing in a div, both with elvis operator and a <div *ngIf = "car"></div>, doesn't work. Even tried with <template *ngIf="car"></template>, well that doesn't work either....

Edit: my mess-up, wrapping like below, it does "kind of" work, meaning, it gives a new error....

Template:

@Component({
    selector: 'car-detail',
    template: `  
    <h1>Car: {{car?.name}}</h1>
    <hr>
    <button (click)="addBrand()">Add Brand</button>
    <div *ngIf="car">      
      <div *ngFor="let brand of car.getBrands(); let i=index">  
      <h2>Brand {{i+1}}</h2>
      </div>   
    </div>   
4
  • Seems I missed that sentence previously. What does happen when you add ? to car?.getBrands()? Do you get the error in initial rendering or when you click the Add Brand button? Commented Jun 4, 2016 at 10:39
  • Did you try adding the ? at both? Might sound like a dumb question but from your question and comments you tried adding ? first at {{car?.name}} and then at ... of car?.getBrands() but it needs to be added at both. Commented Jun 4, 2016 at 10:44
  • Yes I have tried it at both, sorry I haven't been clear. It still gives the same undefined error that it can't read getBrands() of undefined even though I have it on both places. Commented Jun 4, 2016 at 10:49
  • You might try Ctrl+F5 in the browser to force reload. If you can't get rid of the error try to reproduce in a Plunker. Commented Jun 4, 2016 at 11:30

1 Answer 1

1

You mention <h1>Car: {{car?.name}}</h1> with ? but the full code example has <td>{{car.name}}</td> without ? which will cause an error.

<div *ngFor="let brand of car.getBrands(); let i=index">  

also needs a ? to avoid errors when Angular tries to render the view and car is not yet set

<div *ngFor="let brand of car?.getBrands(); let i=index">  
Sign up to request clarification or add additional context in comments.

1 Comment

Umm, yes, I actually do have the car?.name in my template (I edited my question to add it). That's why the error cannot read getBrands of undefined comes, because it can read the car?.name in my template. And in my question I mentioned that I've tried car?.getBrands() but that doesn't work either. Same error.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.