0
votes

1st post here. So I am following the Angular Tour of heroes using Angular 6 and I get how ngModel is working.. for the most part. I just don't get how it is able to change my data in the list when the ngModel is assigned a different variable. Here is my code below:

heroes is assigned list of mock data with a Hero type consisting of ID and name.

This is showing a list of heroes through a variable called hero

When a hero is clicked, selectedHero is assigned a Hero.

From there a the details of a hero is displayed underneath the list.

I understand that when using ngModel in the input changes the selectedHero.name, but how is it able to change the the hero.name in the list and how would I be able to stop it from changing that?

ps, I'm new here, I could not find anything that could answer this. So apologies if I posted this in the wrong place.

heroes.component.html

<h2>My Heroes</h2>
<ul class="heroes">
  <!--calls function when selected and changes the background color to the selected class-->
  <li *ngFor="let hero of heroes"
      (click)="onSelect(hero)"
      [class.selected]="hero === selectedHero">

    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

<div *ngIf="selectedHero">
  <h2>{{ selectedHero.name }}</h2>
  <div>id: {{ selectedHero.id }}</div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="Hero Name">
    </label>
  </div>
</div>

heroes.component.ts

import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes = HEROES;
  selectedHero: Hero; // undefined until selected
  onSelect(hero: Hero) {
    this.selectedHero = hero;
  }

  constructor() { }

  ngOnInit() {
  }

}

Heroes HTML page

1
Because it's all references. selectedHero is nothing but a pointer to an element in your list. If you don't want it to be changed, you probably shouldn't use selectedHero.name in your model, but something else, e.g. public String selectedHeroName;Jan B.
Indeed, it has nothing to do with Angular, and everything to do with how objects and references work in JavaScript (and most OO languages). It pretty much works like in real life. If you have a box containing empty bottles, and you fill the bottles with water, then your box now contains filled bottles.JB Nizet

1 Answers

1
votes

ngModel uses two-way-data binding. That means, that the variable selectedHeros.name in the input is a reference to the hero-item in the list heroes. You don't have a separate variable for the input field. So if you change the value of selectedHero.name in the input field, you change directly the value of the item in the list.

Here is a nice explanation of two-way-data binding. In the example you can see, you can rewrite the ngModel in the input also with this:

<input [value]="selektedHero.name" (input)="selektedHero.name = $event.target.value">

With ngModel you are not able to prevent the change of the variables in the list. But you can rewrite the input to avoid ngModel. For example, you can use the following:

<input [value]="selektedHero.name"></input>

With this, you will have the value of selektedHero.name in the input field, but if you change this value, it will not change the variables in the list.

Comparing the two approaches:

<input [(ngModel)]="selectedEntry">
<br/>
<input [value]="selectedEntry">
<br/>
{{selectedEntry | json}}

Here you have both approaches. If you change the text of the first input field on your website, the value of selectedEntry will change. However, if you change the text in the second input field, the value of selectedEntry will not change (only one-way-data binding). The same holds for @Input directives. In this case, only a reference to the actual variable is created.