Prashant
Prashant

Reputation: 4614

Angular 2+: Child components ts variable changes but UI does not show changed value?

I have a child TestComponent component as follows:

import { Component, OnInit, Input } from '@angular/core';
import { ApiService } from '../../../api.service';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {

  constructor(private apiService: ApiService) { }

  testDisplayMessage = 'No data to show';

  ngOnInit() {
  }

  getMessage(param: string) {
    this.callingTest = true;
    this.apiService.getTest( param ).subscribe( data => {
      this.setTestDisplayMessage( data );
      this.callingTest = false;
    }, err => {
      console.log( JSON.stringify( err ) );
      this.setTestDisplayMessage( 'Failed to get data' );
      this.callingTest = false;
    } );
  }

  setTestDisplayMessage( message: string ) {
    this.testDisplayMessage = message;
  }
}

contents of test.component.html

<p style="padding: 10px;">{{ testDisplayMessage }}</p>

Use in parent componet :

Trigger JS Code in parent component on button click,

import { TestComponent } from './test/test.component';
....

.....
@Component({
  providers: [ TestComponent ],
  templateUrl: 'parent.component.html'
})
export class ParentComponent implements OnInit {
  ...
  constructor(private testComponent: TestComponent) { }
  ...
  // Button on parent template triggers this method
  getMessage() {
    this.testComponent.getMessage('Hello');
  }
  ...
}

Html tag added in parent component,

<app-test></app-test>

When I debugged above code trigger point, call to setTestDisplayMessage() happens the field testDisplayMessage in TestComponent gets changed but UI shows the old message 'No data to show', why is the message on change does not reflect on UI template? Or this is not the way it is supposed to get used? Shall I use @Input

Update:

Based on the pointers given in the following answers as well as comment sections, I changed my component as @ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using @ViewChild, so code changes as follows,

Earlier wrong code

constructor(private testComponent: TestComponent) { }

Solution

@ViewChild(TestComponent)
testComponent: TestComponent;

I found this article useful.

Upvotes: 0

Views: 1012

Answers (4)

Prashant
Prashant

Reputation: 4614

Update:

Based on the pointers given in the following answers as well as comment sections, I changed my component as @ViewChild so in above parent component instead of passing the child component as an argument to constructor I declared it as child component using @ViewChild, so code changes as follows,

Earlier wrong code

constructor(private testComponent: TestComponent) { }

Solution

@ViewChild(TestComponent)
testComponent: TestComponent;

I found this article useful.

Upvotes: 0

lpradhap
lpradhap

Reputation: 653

basically, your approach is wrong, please use Input() or Services to share data between components.

however, if you want to make ur code work, the below may work

  1. import change detector

    constructor(private cdRef: ChangeDetectorRef) { }

note: import reference -> import { ChangeDetectorRef } from '@angular/core';

  1. execute detect change after the value is updated

    setTestDisplayMessage( message: string ) {
        this.testDisplayMessage = message;
        this.cdRef.detectChanges();
      }
    

I hope this helps

Upvotes: -1

Chirag
Chirag

Reputation: 413

Use @ViewChild()

In html file:

<app-test #childComp></app-test>

In parent component.ts file

import { Component, OnInit, ViewChild } from '@angular/core';
....

.....
@Component( {
templateUrl: 'parent.component.html'
} )
export class ParentComponent implements OnInit {

@viewChild('childComp') childComp: any;
constructor() { }
...
// Button on parent template triggers this method
getMessage() {
   this.childComp.getMessage('Hello');
}
...
}

Upvotes: 3

Damian Pioś
Damian Pioś

Reputation: 483

definitely use @Input() but on set method

@Input()
set someProperty(value) {
  // do some code
}

now every time you pass new value here, code will run

Upvotes: -1

Related Questions