Ganapathi Basimsetti
Ganapathi Basimsetti

Reputation: 174

Emitting child component data changes to parent component automatically

I have BusinessComponent(parent) and AddressComponent(child). Now within the AddressComponent, the two way data binding is working fine. Now I require any change in AddressComponent to be emitted to the BusinessComponent as an Addressobject, not an individual property of the Address object. I tried using ngOnChanges() but the doc says this.

Angular only calls the hook when the value of the input property changes. The value of the hero property is the reference to the hero object. Angular doesn't care that the hero's own name property changed. The hero object reference didn't change so, from Angular's perspective, there is no change to report!

And without emitting the data, the parent is detecting the changes of AddressComponent. I couldn't find a way to achieve this.

Here are my code samples.

AddressComponent

import { Component,  EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
import { AppService } from '../services';
import { Address } from '../types';

@Component({
  selector: 'app-address',
  templateUrl: 'address.component.html'
})

export class AddressComponent implements OnInit, OnChanges {

    @Input()
    address: Address;

    @Output()
    addressChange: EventEmitter<Address> = new EventEmitter<Address>();

    constructor(
        private appService: AppService
    ) { super(appService); }

  ngOnInit() {
        this.address = new Address('');
  }
    ngOnChanges(changes: SimpleChanges) {
        // This is not being called for emitting the changes.
        console.log(changes);
        this.addressChange.emit(this.address);
    }
}

AddressComponent Template

<div class="form-row">
     <label class="form-label" for="houseNo">{{ labels['houseNo'] }}</label>
    {{ address.houseNo }}
    <input [(ngModel)] = "address.houseNo" type="text" name="houseNo" id="houseNo" ref-houseNo>
</div>

<div class="form-row">
     <label class="form-label"  for="street">{{ labels['street'] }}</label>
    <input [(ngModel)] = "address.street" type="text" name="street" id="street" ref-street>
</div>

<div class="form-row">
     <label class="form-label"  for="village">{{ labels['village'] }}</label>
    <input [(ngModel)] = "address.village" type="text" name="village" id="village" ref-village>
</div>

<div class="form-row">
     <label class="form-label"  for="city">{{ labels['city'] }}</label>
    <input [(ngModel)] = "address.city" type="text" name="city" id="city" ref-city>
</div>

And I bind the input like this in BusinessComponet

<app-address [(address)]="address"></app-address>.

How to achieve this?

Upvotes: 4

Views: 4040

Answers (1)

AVJT82
AVJT82

Reputation: 73367

As mentioned in comments, you do not need the two way binding or @Output. Since JS objects are mutable, means that reference is same object, BUT, you are doing

ngOnInit() {
  this.address = new Address('');
}

in child, which initialization I do not also understand, since Address (I assume it's a class) has several properties. But if you want to have the same reference you shouldn't do that.

I suggest you use interface for your Address, something like:

export interface Address {
  houseNo: number;
  street: string;
  village: string;
  city: string;
}

also then you can type your object like:

address: Address = {}

in your parent, or then set initial values to it, but seems you want a clean object in your child.

So remove the following from child OnInit and you should be good to go :)

this.address = new Address('');

DEMO

Upvotes: 1

Related Questions