How do I inject a parent component into a child component?

36,282

Solution 1

See @EricMartinez's comment for the answer. The problem seems to be a circular reference when A imports B and B imports A.

Here's a plunker that uses two files instead of the one file that is in Eric's plunker.

The only change from my original plunker is in the ChildComponent:

import {Component, Inject, forwardRef} from 'angular2/core';
// ....
constructor(@Inject(forwardRef(() => AppComponent)) private _parent:AppComponent)

I don't know for sure if this eliminates the circular reference, since A and B are still importing each other, but it seems to work.

See also https://github.com/angular/angular/issues/3216, where Miško states:

This [not user-friendly declaration using forwardRef()] is a limitation of JS and how the function declarations get hoisted. Whenever you have a circular dependency you will need forwardRef :-( I just don't see a away around it.

I would argue that you should not be in situation where your parent needs to know about the children and children need to know about parent. @Query should take care of most of the use cases.

I am sorry, but while I agree this is a pain in some rare cases, I don't see a way out of it, and hence this issue is not actionable, will close.

Hmm... the reason I tried injecting the parent was because I see two ways for a child to communicate with a parent:

  1. the child defines output properties and emits events, which the parent subscribes to
  2. the child injects the parent (e.g., Pane might inject Tabs) and can then call methods on the parent

And I was trying to determine when to use each approach. Miško makes it sound like 2. should be rare.

Update: I was thinking about this some more... 1. is better because there is less coupling between the child and the parent. With 1. the child doesn't need to know (and probably shouldn't know) the public API/interface of the parent.
In the reverse direction (e.g., the parent uses @ViewChild (@Query is now deprecated) to get a reference to the child, then calls methods on the child), the coupling is fine, because the parent is using the child component, so it needs to know the public API/interface of the child: i.e., the input and output properties and public methods.

Solution 2

you could simply use @Host decorator like this:

import {Component, Host} from 'angular2/core';
// ....
constructor(@Host() private app: AppComponent)
Share:
36,282
Mark Rajcok
Author by

Mark Rajcok

I've been writing software for a few decades and I (still) love it. Over the years I've played with mainly Big Data, full-stack web apps, multi-threaded (near) real-time server apps, and embedded software. At the day job, I'm working with Spark, MapR (now HPE Ezmeral), and Hadoop in general. Previously I was doing web dev with D3.js, C3.js, Node.js, Express.js, Python, Bottle.py, MySQL, Apache httpd. For many years prior to that I programmed almost exclusively in C++ (some C++11, some Boost) and Perl (I much prefer Python now). I've dabbled with Go, R, Java, Google AppEngine, AWS, GraphViz, IBM Streams/SPL and BigInsights, Oracle OEP, DynamoDB. I've read books about Google Compute Engine, Heroku, Neo4j, Redis, and MongoDB, and I've taken a few Udacity iOS courses (then I switched to Ionic), but I never got around to actually trying these out (bummer, eh?). I created an online Angular course at udemy.com, with an emphasis on Observables, Change Detection, Dependency Injection, Data Binding, and Inter-Component Communication and Data Flow. Too bad the course is no longer up to date. Current interests/learning: Spark, Docker, Swarm, Go Open source project: Peri$scope, a tool that uses GraphViz to visualize AngularJS prototypal scope inheritance, properties, and services First computer: Commodore64 (I still have one) SO badge I'm most proud of: Sportsmanship My profile picture: my son gave me that lego minifig years ago, but I put the Angular logo on it

Updated on July 09, 2022

Comments

  • Mark Rajcok
    Mark Rajcok almost 2 years

    I'm trying to inject a parent component into a child component. I thought this would be straightforward – simply specify/inject the parent component in the child's constructor():

    constructor(private _parent:AppComponent) {}   // child component constructor
    

    I get the following error:

    EXCEPTION: Cannot resolve all parameters for ChildComponent(?). Make sure they all have valid type or annotations.

    What am I missing?

    ChildComponent:

    import {Component} from 'angular2/core';
    import {AppComponent} from './app.component';
    
    @Component({
      selector: 'child',
      template: `<p>child</p>`
    })
    export class ChildComponent {
      constructor(private _parent:AppComponent) {}
    }
    

    AppComponent:

    import {Component} from 'angular2/core';
    import {ChildComponent} from './child.component';
    
    @Component({
      selector: 'my-app',
      template: `{{title}} <child></child>
      `,
      directives: [ChildComponent]
    })
    export class AppComponent {
      title = "Angular 2 - inject parent";
      constructor() { console.clear(); }
    }
    

    Plunker

  • Eric Martinez
    Eric Martinez over 8 years
    Yep, you're right, but right now your issue wasn't related to that (maybe using circular dependency was a mistake). Your issue was that you had a circular reference (looks like they're different things). So when you did import {A} from 'B' and import {B} from 'A' you had a circular reference. That's why I removed it in the plnkr.
  • Mark Rajcok
    Mark Rajcok over 8 years
    @EricMartinez, I'm a bit confused. My latest plunk still has a circular reference (right?), but it works. Using @Inject and forwardRef() seems to solve the circular reference problem. Is there more to it than that?
  • Eric Martinez
    Eric Martinez over 8 years
    Oh my god... it works!! I didn't know about that, actually I didn't know that forwardRef could solve this issue using two different files... This is indeed a good finding. I must regret everything I've said so far. Thanks for enlighten me! You're right, so don't be confused at all, I was wrong!
  • Benjamin McFerren
    Benjamin McFerren over 8 years
    How would you add <parent></parent> to the child components template?
  • Mark Rajcok
    Mark Rajcok over 8 years
    @BenjaminMcFerren, I wouldn't recommend that, as you'll likely end up in a infinite loop. I believe the only reason a child should (under rare circumstances) get a reference to a parent component would be call public methods/APIs on the parent component.
  • Benjamin McFerren
    Benjamin McFerren over 8 years
    ending up in an infinite loop could be true anytime anyone ever uses recursion in general. This feature was possible with Angular1. Is there a way to do this with Angular2?
  • Jeevanantham
    Jeevanantham over 7 years
    @MarkRajcok @Inject(forwardRef(() => AppComponent)) private _parent:AppComponent works but its not injecting the active instance of parent instead its creating a new instance, so how to get the active instance of parent ?
  • Max
    Max over 7 years
    @Jeevanantham You can only get the active instance if you omit "@Inject(forwardRef(() => AppComponent))" so you have a constructor like this: constructor(private _parent:AppComponent)
  • maxisam
    maxisam almost 7 years
    A better way is to use a library like ngRx to manage this kinda communication.
  • Mojtaba
    Mojtaba over 5 years
    I used @Host and it didn't work; was going to down vote your answer, but there might be some cases that it works. So, please explain more why and when it is going to work.
  • itay oded
    itay oded over 5 years
    what exactly is your case?
  • JohnnyDevNull
    JohnnyDevNull over 4 years
    This injects the host component itself not the parent component which renderes the actual component
  • itay oded
    itay oded over 4 years
    The case above was to inject specifically the AppComponent @Host will go over the view provider and will inject the closest match, so in this case, we will always get the AppComponent, for your case you could use the @SkipeSelf decorator to skip the component itself