Search code examples
angularangular2-componentsangular2-changedetection

Re-render parent component on property change triggered by child


I'm pretty new to Angular and I'm trying to build some re-usable components, like a input text component for example. I want to customise it with all sorts of built-in methods, validations, associated labels and error labels, etc.

I've mostly managed to to it. What I'm trying now to achieve is to re-render the parent component (and all children implicitly) when a property is changed.

I'm getting the callback triggered in the parent and assigning the value to my text property, but the DOM does not update with the new value.

Parent Component

import { Component, Input } from '@angular/core';

@Component({
    selector: 'parent',
    template: '<input-text [text]="text" [onChange]="onChange"></input-text> <p>The text is now {{text}}</p>'
})

export class ParentComponent {
    text: string;

    onChange($text) {
        this.text = $text;
        console.log('onChange', this.text); // <-- it gets here on child input
    }
}

Input Text - Child Component

import { Component, Input } from '@angular/core';

@Component({
    selector: 'input-text',
    template: '<input type="text" [(ngModel)]="text" (ngModelChange)="onChange($event)" [value]="text"/>',
})

export class InputTextComponent {
    @Input() text: string;
    @Input() onChange: Function;
}

So this is it. Writing in the child component triggers the parent's onChange function, I update the text property but the template message does not change.

I'm basically trying to create an uncontrolled component, simliar to React. Also, if I add more input-text children they don't share the same text, although the parent's text property is a single one and theoretically is passed to all children.

What I've tried

I've also tried using ngOnChanges in the child component as mentioned here and to use the changeDetection property as mentioned here but neither worked. The problem appears to be in the parent I also tried using @Input() in the parent component instead for the text property.

I'm pretty sure I'm missing something simple but can't figure out what. When text changes I want to see that in the DOM, and if I use 10 input-text components that get passed the same text property I would expect all of them display that.


Solution

  • Ok, it was simple like I expected. I was looking over this answer as a workaround and noticed that in the parent onChange function it didn't recognise this.chRef or other properties.

    So I immediately realised I wasn't properly binding my function. So I changed the parent template to contain

    [onChange]="onChange.bind(this)"
    

    and it now works properly.

    Nevertheless, if you have suggestions as to how this code could be improved please let me know.