2

To this question asking how to protect [(ngModel)]="currentService.user.username" when the variable is not yet defined, the following answer was given:

<input [(ngModel)]="currentService.user && currentService.user.username">

At first, I thought that this could not work because the bound expression would not be a valid left-hand side in an assignment:

(this.currentService.user && this.currentService.user.username) = "Bob"; // Not valid

However, Pardeep Jain provided a demo which showed that it does work: the variable currentService.user.username is bound to the input element once it is defined. This stackblitz is a forked version of his demo.

My question: is binding a conditional expression this way with [(ngModel)] supposed to work or does it work only "by accident" and may stop working in future versions of Angular?

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146

2 Answers2

2

IMO, data bound expression is correct here in below line -

<input [(ngModel)]="currentService.user && currentService.user.username">

Because when you bind ngModel with such expression (currentService.user && currentService.user.username) , behind the scene it will check for the first value and once it will be available then it will look for second value, if exist angular will bind your value to that ngModel else bind with first one.

(this.currentService.user && this.currentService.user.username) = "Bob"; // Not valid

This is not an valid syntax because here you are assigning value to some variable which you need to describe strongly I mean either this.currentService.user.username or this.currentService.user not using && seprator.

But yes if you write like this

this.bob = this.currentService.user && this.currentService.user.username // will be valid - here this.bob is some variable

Hope you get my point, if any confusion let me know.

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
Pardeep Jain
  • 84,110
  • 37
  • 165
  • 215
  • I am still a little bit confused about the assignment part of two-way binding. I understand that `[ngModel]` would work with the conditional expression. But when the variable is updated with the input content, doesn't it work like an assignment? Apparently not. I don't know how the bound expression can be used like a variable that can be assigned to. – ConnorsFan May 18 '18 at 13:51
2

This is not about Angular, this is about JS.

Look at this snippet :

const variable = {
  user: {
    name: 'John Doe'
  }
};

console.log(variable);
console.log(variable.user);
console.log(variable.secondUser);
console.log(variable.user.name);
console.log(variable.secondUser.name);

As you can see, it tests if the variables are defined. When you try to access a property of a variable that is undefined, an error is being thrown.

What happens in your case, is that if the first condition is missed, the other conditions aren't tested.

Look at this snippet :

const variable = {
  user: {
    name: 'John Doe'
  }
};

console.log(variable && variable.user && variable.user.name);
console.log(variable && variable.user && variable.user.surname);
console.log(variable && variable.secondUser && variable.secondUser.name);

This time, no errors are thrown. A stackoverflow question taught me that.

Second point, the && operator returns the value of the last expression if that expression is truthy or falsy. Look at this snippet :

const variable = {
  user: {
    name: 'John Doe'
  }
};

console.log(variable && variable.user && variable.user.name);
console.log(variable && variable.user && variable.user.surname);
console.log(variable && variable.user && !!variable.user.name);
console.log(variable && variable.user && !!variable.user.surname);

As you can see, if the last condition isn't a boolean, it will return the value of the condition.

By the way, in Angular, you can use the Elvis operator, which allows you to shorten the syntax (but you can't use it in template driven forms) :

<input *ngIf="currentService?.user?.username">
  • And and also, **[MDN explains that](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators)** –  May 18 '18 at 12:30
  • You're right I should have specified that. But since it's a specific case, I'll let it (for discovering purposes) and specify the ngModel case. –  May 18 '18 at 13:27
  • Correction to my previous comment: I often use the `&&` syntax with `[ngModel]` to return the second value (so I should not have been surprised with that). What I am confused with is that the expression becomes a reference to the second variable, allowing it to be updated with the input content. Not sure how that works. – ConnorsFan May 18 '18 at 13:59
  • It returns the variable, so you can instantiate a variable with it such as `let name = variable && variable.user && variable.user.name`. That's why it works with ngModel –  May 18 '18 at 14:03
  • Your example is for the "one-way binding" side (I understand that `[ngModel]="v && v.x"` works). But how does the other side of two-way binding work? In order to update the variable with the input value, it should do something like `v && v.x = input.value`. In normal code, the conditional expression returns the value of the second variable, not the variable itself. If it did, the invalid example in the question would be valid – ConnorsFan May 18 '18 at 14:35
  • I didn't go that deep into angular but I imagine that as I said, it returns a variable, si this equivalent to writing `variable.prop`, which works both ways. –  May 18 '18 at 18:21