Angular2: child component access parent class variable/function

Angular

Angular Problem Overview


I have a variable in the parent component that might be changed by child, parent will be using this variable in the view and thus has to propagate changes.

import {Component, View} from 'angular2/core';

@Component({selector: 'parent'})
@View({
    directives: [Child],
    template: `<childcomp></childcomp>`
})
class Parent {
    public sharedList = new Array();
    constructor() {
    }
}


@Component({selector: 'child'})
@View({template: `...`})
class Child {
    constructor() {
        //access 'sharedList' from parent and set values
        sharedList.push("1");
        sharedList.push("2");
        sharedList.push("3");
        sharedList.push("4");
    }
}

Angular Solutions


Solution 1 - Angular

If you use input property databinding with a JavaScript reference type (e.g., Object, Array, Date, etc.), then the parent and child will both have a reference to the same/one object. Any changes you make to the shared object will be visible to both parent and child.

In the parent's template:

<child [aList]="sharedList"></child>

In the child:

@Input() aList;
...
updateList() {
    this.aList.push('child');
}

If you want to add items to the list upon construction of the child, use the ngOnInit() hook (not the constructor(), since the data-bound properties aren't initialized at that point):

ngOnInit() {
    this.aList.push('child1')
}

This Plunker shows a working example, with buttons in the parent and child component that both modify the shared list.

Note, in the child you must not reassign the reference. E.g., don't do this in the child: this.aList = someNewArray; If you do that, then the parent and child components will each have references to two different arrays.

If you want to share a primitive type (i.e., string, number, boolean), you could put it into an array or an object (i.e., put it inside a reference type), or you could emit() an event from the child whenever the primitive value changes (i.e., have the parent listen for a custom event, and the child would have an EventEmitter output property. See @kit's answer for more info.)

Update 2015/12/22: the heavy-loader example in the Structural Directives guides uses the technique I presented above. The main/parent component has a logs array property that is bound to the child components. The child components push() onto that array, and the parent component displays the array.

Solution 2 - Angular

What about a little trickery like NgModel does with NgForm? You have to register your parent as a provider, then load your parent in the constructor of the child.

That way, you don't have to put [sharedList] on all your children.

// Parent.ts
export var parentProvider = {
    provide: Parent,
    useExisting: forwardRef(function () { return Parent; })
};

@Component({
    moduleId: module.id,
    selector: 'parent',
    template: '<div><ng-content></ng-content></div>',
    providers: [parentProvider]
})
export class Parent {
    @Input()
    public sharedList = [];
}

// Child.ts
@Component({
    moduleId: module.id,
    selector: 'child',
    template: '<div>child</div>'
})
export class Child {
    constructor(private parent: Parent) {
        parent.sharedList.push('Me.');
    }
}

Then your HTML

<parent [sharedList]="myArray">
    <child></child>
    <child></child>
</parent>

You can find more information on the subject in the Angular documentation: https://angular.io/guide/dependency-injection-in-action#find-a-parent-component-by-injection

Solution 3 - Angular

You can do this In the parent component declare:

get self(): ParenComponentClass {
        return this;
    }

In the child component,after include the import of ParenComponentClass, declare:

private _parent: ParenComponentClass ;
@Input() set parent(value: ParenComponentClass ) {
    this._parent = value;
}
 
get parent(): ParenComponentClass {
    return this._parent;
}

Then in the template of the parent you can do

<childselector [parent]="self"></childselector>

Now from the child you can access public properties and methods of parent using

this.parent

Solution 4 - Angular

Basically you can't access variables from parent directly. You do this by events. Component's output property is responsible for this. I would suggest reading https://angular.io/docs/ts/latest/guide/template-syntax.html#input-and-output-properties

Solution 5 - Angular

The main article in the Angular2 documentation on this subject is :

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child

It covers the following:

  • Pass data from parent to child with input binding

  • Intercept input property changes with a setter

  • Intercept input property changes with ngOnChanges

  • Parent listens for child event

  • Parent interacts with child via a local variable

  • Parent calls a ViewChild

  • Parent and children communicate via a service

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKhaledView Question on Stackoverflow
Solution 1 - AngularMark RajcokView Answer on Stackoverflow
Solution 2 - AngularjsgoupilView Answer on Stackoverflow
Solution 3 - AngularFRLView Answer on Stackoverflow
Solution 4 - AngularkitView Answer on Stackoverflow
Solution 5 - AngularSimon_WeaverView Answer on Stackoverflow