89

I have a property in a top level Component that is used data from a HTTP source like so (this is in a file called app.ts):

import {UserData} from './services/user-data/UserData';

Component({
    selector: 'app', // <app></app>
    providers: [...FORM_PROVIDERS],
    directives: [...ROUTER_DIRECTIVES],
    pipes: [],
    template: require('./app.html')
})
@RouteConfig([
    // stuff here
])

export class App {
    // Please note that UserData is an Injectable Service I have written
    userStatus: UserStatus;

    constructor(private userData: UserData) {
        this.userStatus = new UserStatus();
    }

    ngOnInit() {
        this.userData.getUserStatus()
            .subscribe(
            (status) => {
                this.userStatus = status; // I want to access this in my Child Components...
            },
            (err) => {console.log(err);},
            () => {console.log("User status complete");            }
        );
    }
}

Now, I have another Component that is a direct child of the top level Component and within it I would like to access the parent's property 'userStatus', here is the child:

Component({
    selector: 'profile',
    template: require('app/components/profile/profile.html'),
    providers: [],
    directives: [],
    pipes: []
})

export class Profile implements OnInit {
    constructor() {

    }

    ngOnInit() {
        // I want to have access with the parent App Component, 'userStatus' propety here... I only want to read this property
    }
}

Now in Angular 1.x this would be easy as I could reference $parent in my child controller or (ANTI PATTERN ALERT!!!) I could be so foolish to put this data in my $rootScope.

What would be the best way to access the parent in Angular 2?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Mike Sav
  • 14,805
  • 31
  • 98
  • 143

9 Answers9

91

There are different way:

export class Profile implements OnInit {
constructor(@Host() parent: App) {
  parent.userStatus ...
}
  • data-binding
export class Profile implements OnInit {
  @Input() userStatus:UserStatus;
  ...
}

<profile [userStatus]="userStatus">
Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Angular2 is a lot about performance. Angular2 is (or will - there is still a lot to be done) be optimized so even large applications perform well. Also support for TS and Dart (which are better suited for large applications than JS) brings some boilerplate and limitations but this also allows to analyze the code and do smarter optimizations. No light without shadow... – Günter Zöchbauer Feb 08 '16 at 10:59
  • 3
    @GünterZöchbauer, I'm happy you pointed out the "*disadvantage: tight coupling*" of `@Inputs`, but I would've hoped that you acknowledged the **disadvantage(s)** of using Services -- namely, relating to the convenience of Angular's Dependency Injection system. **The Reader should be aware that having a high *Link-Degree* i.e. *Fan-Out/-In* is equally dangerous**. With direct input chaining you're at least obeying *LoD* ( Law of Demeter ), but I'd architect a Sandbox or Director/Facade to *every* module so they don't Fan-Out to a bazillion services; because *that's* when you're really hosed. – Cody Jan 10 '17 at 19:49
  • 1
    Surely the tight coupling issue is with `@Host() parent: App`, not with the data-binding technique of using `@Input()`? – qaisjp Jul 09 '19 at 01:54
  • 1
    Note : if you need @Host() be optional ,your code must be `@Optional @Host() parent: App` – MOH3N Aug 07 '19 at 07:00
52

I had the same problem but I solved it differently. I don't know if it's a good way of doing it, but it works great for what I need.

I used @Inject on the constructor of the child component, like this:

import { Component, OnInit, Inject } from '@angular/core';
import { ParentComponent } from '../views/parent/parent.component';

export class ChildComponent{
    constructor(@Inject(ParentComponent) private parent: ParentComponent){

    }

    someMethod(){
        this.parent.aPublicProperty = 2;
    }
}

This worked for me, you only need to declare the method or property you want to call as public.

In my case, the AppComponent handles the routing, and I'm using badges in the menu items to alert the user that new unread messages are available. So everytime a user reads a message, I want that counter to refresh, so I call the refresh method so that the number at the menu nav gets updated with the new value. This is probably not the best way but I like it for its simplicity.

mikesoft
  • 741
  • 6
  • 14
  • 3
    It's a good workaround but it's not a solution, because in most cases the component parent will varie as you want to reuse your component. – glemiere Feb 11 '18 at 20:49
  • 1
    Note if you have ParentComponent defined in the same file but after the child, you need to either move it to be defined above the child, or if there is a circular reference you'll need to use forwardRef. ie ```@Inject(forwardRef(() => ParentComponent)) parent: ParentComponent``` – parliament Aug 17 '18 at 11:20
  • Is the `@Inject(ParentComponent)` mandatory? It seems unnecessary since the type is specified, am I missing something? – Elie Faës Jul 24 '19 at 08:25
  • If you load your site at the child page, then parent property will be undefined, if it loads from an async action,so you'll need to handle that aspect. – Sean Sep 05 '20 at 07:14
23

I made a generic component where I need a reference to the parent using it. Here's what I came up with:

In my component I made an @Input :

@Input()
parent: any;

Then In the parent using this component:

<app-super-component [parent]="this"> </app-super-component>

In the super component I can use any public thing coming from the parent:

Attributes:

parent.anyAttribute

Functions :

parent[myFunction](anyParameter)

and of course private stuff won't be accessible.

Deunz
  • 1,776
  • 19
  • 32
  • 1
    This was an easy and straightforward solution to get access to the parent from the child component. Thanks. – vadimbog Aug 09 '21 at 19:34
17

You could:

  • Define a userStatus parameter for the child component and provide the value when using this component from the parent:

    @Component({
      (...)
    })
    export class Profile implements OnInit {
      @Input()
      userStatus:UserStatus;
    
      (...)
    }
    

    and in the parent:

    <profile [userStatus]="userStatus"></profile>
    
  • Inject the parent into the child component:

    @Component({
      (...)
    })
    export class Profile implements OnInit {
      constructor(app:App) {
        this.userStatus = app.userStatus;
      }
    
      (...)
    }
    

    Be careful about cyclic dependencies between them.

Jamal
  • 763
  • 7
  • 22
  • 32
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 2
    This does not make much sense to me. you have a property on your parent class Profile then you say you inject it into your child component Profile?!?!? It looks like you are making a binding named userSatus to a component with a selector of profile but you are injecting the App class to that component that has the name of Profile no bindings. From what you describe I would imagine you have a Parent class (the one that defines userStatus) and a child class, the one that sets a local value in it's constuctor and you should be injecting the Parent in the constructor. Is that right or? – Gurnard Oct 24 '19 at 12:01
  • Someone has the link to the documentation on that ? it is working but I can't put a name on it. – Cifren Mar 29 '23 at 14:51
3

Since the parent-child interaction is not an easy task to do. But by having a reference for the parent component in the child component it would be much easier to interact. In my method, you need to first pass a reference of the parent component by calling the function of the child component. Here is the for this method.

The Code For Child Component.

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

    import { ParentComponent } from './parent.component';

    @Component({
      selector: 'qb-child',
      template: `<div class="child"><button (click)="parentClickFun()">Show text Of Parent Element</button><button (click)="childClickFun()">Show text Of Child Element</button><ng-content></ng-content></div>`,
      styleUrls: ['./app.component.css']
    })
    export class ChildComponent  {
      constructor(private el: ElementRef,private rend: Renderer2){

      };

      qbParent:ParentComponent; 
      getParent(parentEl):void{
        this.qbParent=parentEl;
        console.log(this.el.nativeElement.innerText);
      }
      @Input() name: string;
      childClickFun():void{
        console.log("Properties of Child Component is Accessed");
      }

      parentClickFun():void{
        this.qbParent.callFun();
      }
    }

This is Code for parent Component

import { Component, Input , AfterViewInit,ContentChild,ElementRef,Renderer2} from '@angular/core';

import { ChildComponent } from './child.component';

@Component({
  selector: 'qb-parent',
  template: `<div class="parent"><ng-content></ng-content></div>`,
  styleUrls: ['./app.component.css']
})
export class ParentComponent implements AfterViewInit {
  constructor(private el: ElementRef,private rend: Renderer2){

  };
  @Input() name: string;
  @ContentChild(ChildComponent,{read:ChildComponent,static:true}) qbChild:ChildComponent;

  ngAfterViewInit():void{
    this.qbChild.getParent(this);
  }

  callFun():void{
    console.log("Properties of Parent Component is Accessed");
  }

}

The Html Code

<qb-parent>
  This is Parent
  <qb-child>
    This is Child
  </qb-child>
</qb-parent>

Here we pass the parent component by calling a function of the child component. The link below is an example of this method. Click here!

Ayush Agrawal
  • 369
  • 3
  • 9
2

On Angular 6, I access parent properties by injecting the parent via constructor. Not the best solution but it works:

 constructor(@Optional() public parentComponentInjectionObject: ParentComponent){
    // And access like this:
    parentComponentInjectionObject.thePropertyYouWantToAccess;
}
tolga
  • 2,462
  • 4
  • 31
  • 57
1

You can find a reference to a parent component in several ways.

@Input() dialogInput: DialogComponent; 

constructor(
 @Host() private dialogHost: DialogComponent,
 private dialogDirect: DialogComponent,
 @Inject(DialogComponent) private dialogInjected: DialogComponent,
 @Inject(forwardRef(() => DialogComponent)) private dialogForward: DialogComponent
 private injector: Injector
)
      
ngAfterViewInit(){
 const dialogInjector: DialogComponent = this.injector.get<DialogComponent>(DialogComponent);
}

If you want the component to be available also in content projection this doesn't work.. I think you need a service for this so you can use DI

Ron Jonk
  • 706
  • 6
  • 16
0

You can also add static before property name Eg - static myVariable = 5 and import parent component in child component and then just access it using parentcomponent.myVariable; Static method will make that property available outside component.

-1

Declare #contentChild on the child component, then on the parent declare @ContentChild('contentChild') childContent;

Then: this.childContent.whatever gives you full access to child.

Stephen Poole
  • 159
  • 1
  • 2