New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Binding to undefined object produces error, but binding to empty object cast to interface is OK #4134

Closed
johnpapa opened this Issue Sep 11, 2015 · 18 comments

Comments

Projects
None yet
9 participants
@johnpapa
Copy link
Contributor

johnpapa commented Sep 11, 2015

When I go to the hero details component (see below), I get a binding error briefly in the console because the Hero.name binding fails as the Hero is not yet back from the promise from the http service (however briefly). Makes sense. I prefer not to ngIf things either. So I did this to make the Hero initialize to an object cast to the Hero interface. Should we have to do that?

  hero: Hero = <Hero>{};

and the original code ...

import {Component, View} from 'angular2/angular2';
import {RouteParams} from 'angular2/router';
import {Hero} from './hero';
import {HeroService} from './hero-service';
import {HERO_DIRECTIVES} from './hero-directives';

@Component({selector: 'my-hero-detail'})
@View({
  templateUrl: 'app/hero-detail-component.html',
  directives: [HERO_DIRECTIVES]
})
export class HeroDetailComponent {
  hero: Hero; // this is what I changed to cast it

  constructor(private _heroService: HeroService, private _routeParams: RouteParams) {
    let id = +_routeParams.get('id');
    this._heroService.getHero(id).then(hero => this.hero = hero);
  }
}
@johnpapa

This comment has been minimized.

Copy link
Contributor

johnpapa commented Sep 11, 2015

CC / @wardbell

@pkozlowski-opensource

This comment has been minimized.

Copy link
Member

pkozlowski-opensource commented Sep 13, 2015

I get a binding error briefly in the console because the Hero.name binding fails as the Hero is not yet back from the promise

@johnpapa are you talking about a binding in a template? Sth like {{hero.name}}? If so you can use the Elvis operator - sth like {{hero?.name}}.

Should we have to do that?

Hope that the above answers the question so going to close this one for now. Happy to re-open if more discussion is needed and / or your use-case is different.

@johnpapa

This comment has been minimized.

Copy link
Contributor

johnpapa commented Sep 13, 2015

Yes, the ? helps.

But why does casting it to an empty object to that type solve this, even tho the properties do not exist on it?

@pkozlowski-opensource

This comment has been minimized.

Copy link
Member

pkozlowski-opensource commented Sep 13, 2015

@johnpapa I'm still not sure what is your exact binding error / code in the template, so hard to answer your questions... Could you share the code that triggers the error and the error itself?

Did you have a look at the discussion about the introduction of the Elvis operator I've linked? #791? There was a lengthy discussion in this issue with different pros and cons of throwing when accessing properties of null.

@johnpapa

This comment has been minimized.

Copy link
Contributor

johnpapa commented Sep 13, 2015

Yes ... that discussion explains the Elvis operator.

the html just has a ng-model on the hero. Since it is coming back asyncly, it is not there for a tick, and thus could fail. So the ? would help that. I am just curious why this fixes it too hero: Hero = <Hero>{};

@pkozlowski-opensource

This comment has been minimized.

Copy link
Member

pkozlowski-opensource commented Sep 13, 2015

I am just curious why this fixes it too hero: Hero = {};

Well, assuming that in your template you've got sth like {{hero.name}} in a template the hero: Hero = <Hero>{}; line "works" since it initialises the hero field to an empty object instead of null.

@johnpapa

This comment has been minimized.

Copy link
Contributor

johnpapa commented Sep 13, 2015

Yes, that is what is happening. I guess it just seems "odd" to me. Thanks.

@pkozlowski-opensource

This comment has been minimized.

Copy link
Member

pkozlowski-opensource commented Sep 13, 2015

Yeh, I'm still on the fence when it comes to the whole elvis operator story.... I guess we need to hear all the input / opinions based on everyday use of the framework.

@johnpapa

This comment has been minimized.

Copy link
Contributor

johnpapa commented Sep 13, 2015

Agreed.

I can see the value in it, and I kinda like that it is an explicit expression of what could be null at some point. But I can also see times where I would have to put it in several times in a template OR choose to hide it with an ng-if. The casting in the component solves it, too, I guess.

Yes, this is closed. I'm just thinking it all through.

@wardbell

This comment has been minimized.

Copy link
Contributor

wardbell commented Sep 15, 2015

I read the discussion and am frankly amazed that Elvis remains in the building. The syntax does little for me and adds friction.

This case reaffirms it. Why is it important that I can tell Ng that the 'herocan be null but can't tell it that thehero.name` can be null?

For consistency I should be equal obliged to suffix with Elvis like this.

hero?.name?

I don't believe that is legal template syntax and we know there is no binding error for name when we omit it.

Or did I miss the trailing ? option. 😏

@langley-agm

This comment has been minimized.

Copy link

langley-agm commented Jan 15, 2016

My two cents @wardbell , the reason why you don't need the Elvis operator on the name property is because you can parse a null, in the case of Angular, it parses null in the presentation as an empty string, which is a nice behaviour. However you cannot access a property of a null object, that's why you use the elvis in the parent as a null check shortcut, not as a null parser.

@vaudy

This comment has been minimized.

Copy link

vaudy commented Apr 11, 2016

I got the same issue and Im not satisfied with the solution :/

maybe a better solution is to [ngIf]="hero != null" the parent that contains {{hero.name}}?

@vaudy

This comment has been minimized.

Copy link

vaudy commented Apr 11, 2016

I've try it and its work, Its also cleaner for unit test :)

@vamsi15

This comment has been minimized.

Copy link

vamsi15 commented Dec 27, 2017

but for [(ngModel)]='hero?.name"
is it WOrks?

@mlc-mlapis

This comment has been minimized.

Copy link

mlc-mlapis commented Dec 27, 2017

@vamsi15 ... no.

@vamsi15

This comment has been minimized.

Copy link

vamsi15 commented Dec 28, 2017

(ngModelChange)=hero?.name? hero.name=$event:null is not working when input is emptied

@sau9211

This comment has been minimized.

Copy link

sau9211 commented Jun 27, 2018

in .ts file while declaring object of class type i did below
heroes :hero= new hero;
that helps me

@fahadash

This comment has been minimized.

Copy link

fahadash commented Nov 15, 2018

I have a input box bound to a property path like so, and the trailing elvis operator is what I need

<input type="text" [value]="path?.to?.mypropertyThatIsANumber" />

and my textbox says "undefined" instead of empty.

I tried adding a .toString() with the elvis operator and it works

<input type="text" [value]="path?.to?.mypropertyThatIsANumber?.toString()" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment