11

It seems there is no way to watch changes in the parent component when using two-way data binding.

I have a custom input component for collecting a tag list. Two-way data binding is setup and working between this component and its parent.

// the parent component is just a form
// here is how I'm adding the child component
<input-tags formControlName="skillField" [(tags)]='skillTags' (ngModelChange)="skillTagUpdate($event)">
</input-tags>

In the parent component how do you watch the bound variable for changes? While it's always up to date (I've confirmed this) I cannot find any guidance on reacting to changes.

I've tried:

ngOnChanges(changes: SimpleChanges) {
    if (changes['skillTags']) {
        console.log(this.skillTags);  // nothing
    }
}

And

skillTagUpdate(event){
    console.log(event); // nothing
}

UPDATE: TWDB IMHO is not what it is advertised to be. Whenever I arrive at this place where TWDB seems to be a solution I rearchitect for a service and or observable communication instead.

HDJEMAI
  • 7,766
  • 41
  • 60
  • 81
Ben Racicot
  • 4,039
  • 8
  • 42
  • 96
  • add your parent component and html as well – Reza May 15 '19 at 17:40
  • There's really nothing else relevant, they are two very small components. Would you like to see the entire custom input (child) component? Or perhaps a StackBlitz representation? – Ben Racicot May 15 '19 at 17:46
  • if you create stackblitz it will be great – Reza May 15 '19 at 17:48
  • Ok I've added a link to the parent component in SB, thanks for helping me learn this! – Ben Racicot May 15 '19 at 17:58
  • If you are create a custom form control well formed, is two binding way. You can control like another control. If it's a reactive form, I suggest the form `myForm.get('cntrolname').valueChange.subscribe(res=>{...}) else (ngModelChange) must be work – Eliseo Jun 18 '19 at 18:31
  • Do you have a link to stackblitz? – DavidZ Jun 20 '19 at 15:57
  • have you tried to add @output and emit the changes from output that can be handle by parent component – paras shah Jun 21 '19 at 13:25
  • Might be missing something here, but it seems like the assumption is being made, which makes sense, that ngOnChanges will be triggered on two way data binding changes. However, it appears that is not the case. Thus if you want to listen for changes on two way data binding you will need to implement `@Output() change: EventEmitter = new EventEmitter()` along with the @Output for the two way data binding. At which point it begs the question of what value is there in custom two way data binding? – James Parker Mar 18 '21 at 12:00
  • Ha yeah exactly @JamesParker the conclusion I came to also. – Ben Racicot Mar 18 '21 at 18:18

5 Answers5

11

When you implement a two way binding of your own, you have to implement an event Emitter. The syntax for that is mandatory.

this means that you have a hook to listen to if the value changes.

Here is a demo :

<hello [(name)]="name" (nameChange)="doSomething()"></hello>
_name: string;
@Output() nameChange = new EventEmitter();

set name(val) {
  this._name = val;
  this.nameChange.emit(this._name);
}

@Input()
get name() {
  return this._name;
}

counter = 0;

ngOnInit() {
  setInterval(() => {
    this.name = this.name + ', ' + this.counter++;
  }, 1000);
}

Stackblitz

From what I know, this seems the less annoying way to use it, and any two way binding will follow the same rule no matter what, i.e. it ends with the Change word !

HDJEMAI
  • 7,766
  • 41
  • 60
  • 81
  • Ok, so two-way binding is literally only the banana in a box convenience and the same setup as manual binding. Because this is how you setup standard @Output changes. – Ben Racicot Jun 23 '19 at 18:59
2

Your implementation is actually not two-way databinding, the parent and child component are just sharing a reference on the same skillTags variable.

The syntax [(tags)]='skillTags' is syntaxic sugar for [tags]='skillTags' (tagsChange)='skillTags = $event'

You need to implement tagsChange in the child component like this: @Output('tagsChange') tagsChange = new EventEmitter<any>();, then any time you want to modify tags into the children component, dont do it directly, but use this.tagsChange.emit(newValue) instead.

At this point, you'll have real two-way databinding and the parent component is the unique owner of the variable (responsible for applying changes on it and broadcasting changes to the children).

Now in your parent component, if you want to do more than skillTags = $event (implicitly done with [(tags)]='skillTags'), then just add another listener with (tagsChange)='someFunction($event)'.

StackBlitz Demo

Guerric P
  • 20,579
  • 2
  • 28
  • 66
1

Don't know if this is what you're looking for, but have you tried using @Input()?

In child component

@Input() set variableName(value: valueType) {
  console.log(value);
}

In parent component

<input-tags formControlName="skillField" [(tags)]='skillTags'
[variableName]="skillTagUpdate($event)"></input-tags>

The input function is called every time the object binded to the function is changed.

kebbaben
  • 334
  • 1
  • 12
0

1.you can use output(eventemitter)

2.easiest solution is rxjs/subject. it can be observer and observable in same time

Usage:

1.Create Subject Property in service:

import { Subject } from 'rxjs';

export class AuthService {
   loginAccures: Subject<boolean> = new Subject<boolean>();
}

2.When event happend in child page/component use :

logout(){
  this.authService.loginAccures.next(false);
}

3.And subscribe to subject in parent page/component:

constructor(private authService: AuthService) {
    this.authService.loginAccures.subscribe((isLoggedIn: boolean) => {this.isLoggedIn = isLoggedIn;})
}

Update

for two-way binding you can use viewchild to access to your child component items and properties

<input-tags #test></<input-tags>

and in ts file

  @ViewChild('test') inputTagsComponent : InputTagsComponent;

save()
{
   var childModel = this.inputTagsComponent.Model;
}
moha noorani
  • 114
  • 6
  • Hey @moha this question is specific to two-way data binding. – Ben Racicot Jun 18 '19 at 13:23
  • @BenRacicot what is this statement quote from user 'It seems there is no way to watch changes in the parent component'. subject is the best option. you still say my answer is not relevant?? – moha noorani Jun 18 '19 at 14:12
  • It's a good answer but my question was asked specifically because what is the point of two-way data binding if you cannot get the value in the parent? An alternative is nice but not related to the question. (I've voted you up) – Ben Racicot Jun 18 '19 at 14:31
-1

you could listen to the change:

<input-tags formControlName="skillField" [tags]='skillTags' (tagsChange)='skillTags=$event; skillTagUpdate();'></input-tags>

or use getter and setter:

get skillTags(): string {
    return ...
}
set skillTags(value) {
    variable = value;
}

another approach:

 export class Test implements DoCheck {
  differ: KeyValueDiffer<string, any>;
  public skillTags: string[] = [];
  ngDoCheck() {
    const change = this.differ.diff(this.skillTags);
    if (change) {
      change.forEachChangedItem(item => {
        doSomething();
      });
    }
  }
  constructor(private differs: KeyValueDiffers) {
    this.differ = this.differs.find({}).create();
  }
}}
Geggi632
  • 524
  • 3
  • 5