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';
}
}
Finally, the solution proposed by Günter worked after adding UpdateService
as a provider
in the @NgModule
of app.module.ts
We can click the “Call Function” button. It will call the function in the first component. Now we can click the second component link and click the “Call First Component Function” button.
The short answer is to use ViewChild to call the function on component B.
First, create a service product-service with property a product$ field behavior subject to keep the product's value and declare a variable selectedProduct as observable from the product behavior subject. Next, create a new method, setProduct , to set the selected product and update the behavior.
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);
});
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With