Alexander Soare
Alexander Soare

Reputation: 3267

Why isn't my input bound property changing in my angular child component?

I have a parent component with two children.

The parent component has a property which is actually a dictonary like this:

dict = {a: 0, b: 0, c: 0};

I do input binding so that both of the child components can have access to dict.

<app-child-a [dict]="dict"></app-child-a>
<app-child-b [dict]="dict"></app-child-b>

Child A changes a property of dict

@Input() dict: any;

someFunctionInChildA() {
  this.dict.b = 1;
}

I can verify that the Parent component knows about this change.

ngAfterInit() {
  setInterval(() => console.log(this.dict), 1000);
}

This prints 0s until someFunctionInChildA is triggered after which it prints 1s.

Here's my problem: child B doesn't seem to see the change. Here's some code from child B

@Input() dict: any;

ngAfterInit() {
  setInterval(() => console.log(this.dict), 1000);
}

This prints 0s all the time, even after someFunctionInChildA has been triggered.

Do I not understand how property binding works? Or is it more likely that I have a silly or unrelated bug elsewhere?

Upvotes: 0

Views: 833

Answers (2)

Alexander Soare
Alexander Soare

Reputation: 3267

I've figured this one out, and I doubt anyone else will find this useful as it's a rare thing to happen...

A couple of days ago I actually made a service which would save and update dict into Firebase, and this would sync up with my components via rxjs. But I left some of the old logic behind (that is, the logic whereby dict was being managed by the component rather than the service).

I will mark this as the correct answer, but the real credit goes to @bryan06 for his good explanation of the correct pattern for this sort of application.

Upvotes: 0

bryan60
bryan60

Reputation: 29345

as you can see here: https://stackblitz.com/edit/angular-7-master-nxys2f?file=src/app/hello.component.ts

the code in question works fine. BUT.. this is a very bad practice. as this sort of at a distance mutation creates hard to reason about code and hard to understand bugs. I'm guessing your real application is a bit more complex, and you're probably accidentally breaking the reference somewhere. This is why you should use a shared service model and rxjs to share information between components:

@Injectable()
export class DictService {
  private dictSource = new BehaviorSubject({a: 0, b: 0, c: 0})
  dict$ = this.dictSource.asObservable()
  setDict(dict) {
    this.dictSource.next(dict)
  }
}

then inject into your components and subscribe:

constructor(private dictService: DictService) {
  this.dictSub = this.dictService.dict$.subscribe(dict => this.dict = dict) // don't forget to unsubscribe!
}

and use the service to update:

someFunctionInChildA() {
  this.dict.b = 1;
  this.dictService.setDict(this.dict)
}

Upvotes: 2

Related Questions