25

I'm getting a strange error in my app. It's supposed to print out {{thing.title}} from an object, but it shows an error in the console:

EXCEPTION: TypeError: l_thing0 is undefined in [{{thing.title}} in AppComponent@4:44]

I'm not sure where l_thing0 is coming from. If I try to show {{thing}}in the page, it displays [object Object]. If I try to JSON.stringify(this.thing) (see the showObj() function), it correctly displays the stringified object. But if I try to access an attribute like {{thing.title}} I get the error that l_thing0 is undefined.

Here is app.component.ts:

import {Component, OnInit} from 'angular2/core';
import {Thing} from './thing';
import {ThingService} from './thing.service';
import {SubThingComponent} from "./subthing.component";

@Component({
    selector: 'thing-app',
    template: `
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <h1>{{thing.title}} <a href="#" (click)="showObj()" class="btn btn-default">Show Stringified Obj</a></h1>
                    <subthing></subthing>
                </div>
            </div>
        </div>
    `,
    styles:[`

    `],
    directives: [SubThingComponent],
    providers: [ThingService]
})

export class AppComponent implements OnInit {

    constructor(private _thingService: ThingService) { }

    public thing: Thing;

    showObj() {
        // This correctly shows the stringified object
        alert(JSON.stringify(this.thing));
    }

    getThing() {
        this._thingService.getThing().then(thing => this.thing = thing);
        // This correctly logs the object
        setTimeout(() => {console.log('thing', this.thing)}, 5000);
    }

    ngOnInit() {
        this.getThing();
    }
}

Any ideas?

Roham Rafii
  • 2,929
  • 7
  • 35
  • 49
R891
  • 2,550
  • 4
  • 18
  • 30

4 Answers4

55

The issue is that the first time you load the page, thing is still undefined, it gets set to its real value later on asyncronously, so the first time it tries to access the property, it will throw an exception. The ? elvis operator is a shortcut to a nullcheck:

{{thing?.title}}

But its usually a best idea more performant not even try to render the component at all until you have the real object by adding something like:

<h1 *ngIf="thing">

to the container.

Langley
  • 5,326
  • 2
  • 26
  • 42
  • so you mean we have to use `*ngIf` instead of `?` right ? – Pardeep Jain Jan 29 '16 at 05:34
  • 1
    this solution works, but how would you avoid the DOM jigger that results as values are loaded in? Is there an elegant way to have some text like "Loading" in the tag and get swapped out once the values load in? – code4cause Dec 05 '16 at 06:55
  • Sorry I never saw your message until now. That's totally independent, if you have data asynchronously loaded that would happen regardless of null checks or not. For some designers this is ok and you should load and show data as you get it. Personally, I hate this flickering and I prefer to load the entire page once the info is ready. The best way I've found to avoid this and its what I use, is the resolve functionality on routers like ui-router, those will hold off from initiating the components until the model has been loaded by all the registered resolves. – Langley Aug 31 '17 at 22:45
3

though I am coming late to the party but thought this might help for those who're using Angular 5. Strangely noticed the elvis operator will not work in *ngFor loop, but works for *ngif. So here's my code to address the issue.

<div *ngIf = "fullObject?.objProperty">
      <h1>{{fullObject.objProperty1}}</h1>
      <div *ngFor = "let o of fullObject.objPropertyArray">
          <h3>{{o._subheader}}</h3>
          <p>
              {{o._subtext}}
          </p>
        </div>
  </div>
</div>
sagars01
  • 356
  • 1
  • 9
0

You could also set a new variable isFinished to false before retrieving data from server or async function and after receiving the data set it to true. Then put isFinished variable in *ngIf to check if server/function process is done?

0

I am using Angular 8 and although the data is shown, I also had this error and googling it brought me to this question. The accepted answer (though well explained) did not solve it for me. (Maybe because of a newer version, or the use with *ngFor) It does not suffice to just use *ngIf, as you can see:

throws error: v1

  <div class="row">
    <pre>
      <li *ngFor="let obj of object.content">{{obj | json}}</li>
    </pre>
  </div>

throws error: v2

  <div class="row" *ngIf="object.content">
    <pre>
      <li *ngFor="let obj of object.content">{{obj | json}}</li>
    </pre>
  </div>

throws error: v3

  <div class="row" *ngIf="object.content">
    <pre>
      <li *ngFor="let obj of object?.content">{{obj | json}}</li>
    </pre>
  </div>


no error: v1

  <div class="row">
    <pre>
      <li *ngFor="let obj of object?.content">{{obj | json}}</li>
    </pre>
  </div>

no error: v2

  <div class="row" *ngIf="object?.content">
    <pre>
      <li *ngFor="let obj of object.content">{{obj | json}}</li>
    </pre>
  </div>

no error: v3

  <div class="row" *ngIf="object?.content">
    <pre>
      <li *ngFor="let obj of object?.content">{{obj | json}}</li>
    </pre>
  </div>
connectedMind
  • 421
  • 4
  • 17