4

In angular the @Input on a property in combination with the [propertyNameInParent] = ”propertyNameInChild” allows us to bind the value of a parent property to a child property. Also know as one-way binding. Strangely, in my case it is also updating the property in the parent class. How is this possible?

Parent component -> favorite-cities Child component -> city-detail

So basically on a (click) event the favorite city is select (selectedCity property). Then in the city-detail component the city can receive likes by clicking the onPlusIconClick(). Somehow this also triggers the city.rating in the favorite-cities to go up as well. Why don't I have to use an eventemitter to send the updated value back to the parent component?

favorite-cities.component.ts

import {Component, OnInit} from '@angular/core';
import {CitiesService} from '../../services/cities.service';
import {City} from '../../model/city';

@Component({
selector: 'app-favorite-cities',
templateUrl: './favorite-cities.component.html',
styleUrls: ['./favorite-cities.component.css']
})
export class FavoriteCitiesComponent implements OnInit {

cities: City[];
selectedCity: City;

constructor(private citiesService: CitiesService) {
}

ngOnInit() {
    this.citiesService.getCities().subscribe(
        (data) => this.cities = data,
        (err) => console.log(err),
        () => { }
    );
}

setSelectedCity(city: City): void {
    this.selectedCity = city;
}

}

favorite-cities.component.html

<div class="container-fluid">
    <div class="row justify-content-center">
        <div class="col-4">
            <h2>Mijn favoriete steden zijn: </h2>
            <ul class="list-group" *ngFor="let city of cities">
                <li class="list-group-item" (click)="setSelectedCity(city)">{{city.id}} - {{city.name}}
                    <span class="float-right badge badge-success">Likes {{city.rating}} .   </span>
                </li>
            </ul>
        </div>
        <div class="col-4">
            <app-city-detail [selectedCity]="selectedCity"></app-city-detail>
        </div>
    </div>
</div>

city-detail.component.ts

import {Component, Input, OnInit} from '@angular/core';
import {City} from '../../model/city';
import {CitiesService} from '../../services/cities.service';

@Component({
selector: 'app-city-detail',
templateUrl: './city-detail.component.html',
styleUrls: ['./city-detail.component.css']
})
export class CityDetailComponent implements OnInit {

@Input()
selectedCity: City;

constructor(private citiesService: CitiesService) {
}

ngOnInit() {
}

onMinusIconClick(): void {
    if (this.selectedCity.rating > 0) {
        this.selectedCity.rating -= 1;
    }
    this.citiesService.updateRatingOfCity(this.selectedCity).subscribe(
        (data) => { console.log('oaksdoaks'); },
    );

}

onPlusIconClick(): void {
    this.selectedCity.rating += 1;
    this.citiesService.updateRatingOfCity(this.selectedCity).subscribe();
}
}

city-detail.component.html

<div *ngIf="selectedCity">
<div class="container-fluid">
    <div class="row">
        <h2>City details</h2>
        <div class="col-1">
            <i (click)="onPlusIconClick()" class="fas fa-2x fa-plus-square"></i>
        </div>
        <div class="col-1">
            <i (click)="onMinusIconClick()" class="fas fa-2x fa-minus-square"></i>
        </div>
    </div>
</div>
<ul class="list-group">
    <li class="list-group-item">Naam: {{selectedCity.id}} </li>
    <li class="list-group-item">Provincie: {{selectedCity.provincie}}</li>
    <li class="list-group-item">Highlights: {{selectedCity.highlights}} </li>
</ul>
<img class="img-fluid" src="assets/img/{{selectedCity.name.trim()}}.jpg" >

1 Answer 1

5

Because you are inputting an object, and this is just a reference. So the parent and the child have a property referencing the exact same object.

If this is unwanted behaviour, you have to create a shallow copy when setting the selected city. Or in any other place where you update.. There are plenty of options:

setSelectedCity(city: City): void {
  this.selectedCity = { ...city }; //shallow copy
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.