12

I'm able to change the value of a component's variable from another component as console.log() shows in this example.

My problem is that second component's view does not refresh despite the variable change.

Example:

first.component.ts

import {Component} from '@angular/core'; import {SecondComponent} from './second.component'; @Component({ selector: 'first', template: ` <h2>First component</h2> <button (click)="changeOutside()">Change</button> `, providers: [SecondComponent] }) export class FirstComponent { constructor(private otherComponent: SecondComponent) {} changeOutside() { this.otherComponent.change(); } } 

second.component.ts

import {Component} from '@angular/core'; @Component({ selector: 'second', template: ` <h2>Second component</h2> <div>Pet: {{animal}}</div> <button (click)="change()">Change</button> ` }) export class SecondComponent { animal: string = 'Dog'; change() { this.animal = 'Cat'; console.log(this.animal); } } 

Both components are totally unrelated from different branches of the DOM tree.

I've also tried with the first component emitting an event and the second component subscribing to it with the same result, the variable changes but view does not update.


After Günter's suggestion (thanks), I've tried with the next solution with no success. Even the console.log is not working.

first.component.ts

import {Component} from '@angular/core'; import {UpdateService} from './update.service'; @Component({ selector: 'first', template: ` <h2>First component</h2> <button (click)="changeOutside()">Change</button> ` }) export class FirstComponent { constructor(private updateService: UpdateService) {} changeOutside() { this.updateService.changeAnimal('Cat'); } } 

update.service.ts

import {Injectable} from '@angular/core'; import {Subject} from 'rxjs/Subject'; @Injectable() export class UpdateService { // Observable string sources private newAnimalSource = new Subject<string>(); // Observable string streams newAnimal$ = this.newAnimalSource.asObservable(); changeAnimal(animal: string) { this.newAnimalSource.next(animal); } } 

second.component.ts

import {Component} from '@angular/core'; import {UpdateService} from './update.service'; import {Subscription} from 'rxjs/Subscription'; import {Subject} from 'rxjs/Subject'; @Component({ selector: 'second', template: ` <h2>Second component</h2> <div>Pet: {{animal}}</div> <button (click)="change()">Change</button> ` }) export class SecondComponent { animal: string = 'Dog'; subscription: Subscription; constructor(private updateService: UpdateService) { this.subscription = updateService.newAnimal$.subscribe( animal => { console.log(animal); this.animal = animal; }); } change() { this.animal = 'Cat'; } } 

Solved

Finally, the solution proposed by Günter worked after adding UpdateService as a provider in the @NgModule of app.module.ts

2 Answers 2

7

update

I'm pretty sure the cause of your problem is that you expect

constructor(private otherComponent: SecondComponent) {} 

to do something that it actually doesn't do at all.

With this constructor you get a SecondComponent instance injected that is not related to the component shown in your page. This is why your view isn't updated. You didn't update the visible component but an entirely unrelated class.

You need to use a different strategy to get a reference to the other component.

The best way is to not get a reference to the component at all but instead inject a shared service to both and communicate using observables.

For more details refer this official angular 2 cookbook

original

If both components are "totally unrelated", I assume this means they are from individually bootstrapped module and therefore inside different Angular2 applications on the same page.

In this case these components run in different zones and the click event will only cause change detection in the calling component but not in the callee component.

You need to invoke change detection explicitly in the callee to get the view updated. For example like

import {Component, ChangeDetectorRef} from '@angular/core'; @Component({ selector: 'second', template: ` <h2>Second component</h2> <div>Pet: {{animal}}</div> <button (click)="change()">Change</button> ` }) export class SecondComponent { constructor(private cdRef:ChangeDetectorRef){} animal: string = 'Dog'; change() { this.animal = 'Cat'; console.log(this.animal); this.cdRef.detectChanges(); } } 

or

import {Component, NgZone} from '@angular/core'; @Component({ selector: 'second', template: ` <h2>Second component</h2> <div>Pet: {{animal}}</div> <button (click)="change()">Change</button> ` }) export class SecondComponent { constructor(private zone:NgZone){} animal: string = 'Dog'; change() { this.zone.run(() => { this.animal = 'Cat'; console.log(this.animal); }); } } 
Sign up to request clarification or add additional context in comments.

4 Comments

I'm sorry I have not explained myself well. With totally unrelated I tried to reflect that they aren't parent or child of each other. Anyway, I will try your solution. Thanks.
My solution is only applicable if they are in different Angular2 applications.
UpdateService should be added to providers of an @NgModule(...) decorator. In your case if you add it to both components, both components get a different instance. If one of the components is a parent of the other, you can also provide it at the parent only or another component that is a common parent (ancestor) of both.
That was it!, added UpateService as provider for @NgModule in app.module.ts and it works. Thank you very much for your accurate and quick answers.
1

Solved

As I edited in the question, the solution proposed by Günter, which is the second series of files, worked after adding UpdateService as a provider in the @NgModule of app.module.ts

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.