Wait for Angular 2 to load/resolve model before rendering view/template

shannon picture shannon · Jan 11, 2016 · Viewed 99.7k times · Source

In Angular 1.x, UI-Router was my primary tool for this. By returning a promise for "resolve" values, the router would simply wait for the promise to complete before rendering directives.

Alternately, in Angular 1.x, a null object will not crash a template - so if I don't mind a temporarily incomplete render, I can just use $digest to render after the promise.then() populates an initially empty model object.

Of the two approaches, if possible I'd prefer to wait to load the view, and cancel route navigation if the resource cannot be loaded. This saves me the work of "un-navigating". EDIT: Note this specifically means this question requests an Angular 2 futures-compatible or best-practice method to do this, and asks to avoid the "Elvis operator" if possible! Thus, I did not select that answer.

However, neither of these two methods work in Angular 2.0. Surely there is a standard solution planned or available for this. Does anyone know what it is?

@Component() {
    template: '{{cats.captchans.funniest}}'
}
export class CatsComponent {

    public cats: CatsModel;

    ngOnInit () {
        this._http.get('/api/v1/cats').subscribe(response => cats = response.json());
    }
}

The following question may reflect the same issue: Angular 2 render template after the PROMISE with data is loaded . Note that question has no code or accepted answer in it.

Answer

tehaaron picture tehaaron · Jan 12, 2016

Try {{model?.person.name}} this should wait for model to not be undefined and then render.

Angular 2 refers to this ?. syntax as the Elvis operator. Reference to it in the documentation is hard to find so here is a copy of it in case they change/move it:

The Elvis Operator ( ?. ) and null property paths

The Angular “Elvis” operator ( ?. ) is a fluent and convenient way to guard against null and undefined values in property paths. Here it is, protecting against a view render failure if the currentHero is null.

The current hero's name is {{currentHero?.firstName}}

Let’s elaborate on the problem and this particular solution.

What happens when the following data bound title property is null?

The title is {{ title }}

The view still renders but the displayed value is blank; we see only "The title is" with nothing after it. That is reasonable behavior. At least the app doesn't crash.

Suppose the template expression involves a property path as in this next example where we’re displaying the firstName of a null hero.

The null hero's name is {{nullHero.firstName}}

JavaScript throws a null reference error and so does Angular:

TypeError: Cannot read property 'firstName' of null in [null]

Worse, the entire view disappears.

We could claim that this is reasonable behavior if we believed that the hero property must never be null. If it must never be null and yet it is null, we've made a programming error that should be caught and fixed. Throwing an exception is the right thing to do.

On the other hand, null values in the property path may be OK from time to time, especially when we know the data will arrive eventually.

While we wait for data, the view should render without complaint and the null property path should display as blank just as the title property does.

Unfortunately, our app crashes when the currentHero is null.

We could code around that problem with NgIf

<!--No hero, div not displayed, no error --> <div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>

Or we could try to chain parts of the property path with &&, knowing that the expression bails out when it encounters the first null.

The null hero's name is {{nullHero && nullHero.firstName}}

These approaches have merit but they can be cumbersome, especially if the property path is long. Imagine guarding against a null somewhere in a long property path such as a.b.c.d.

The Angular “Elvis” operator ( ?. ) is a more fluent and convenient way to guard against nulls in property paths. The expression bails out when it hits the first null value. The display is blank but the app keeps rolling and there are no errors.

<!-- No hero, no problem! --> The null hero's name is {{nullHero?.firstName}}

It works perfectly with long property paths too:

a?.b?.c?.d