0

I'm unable to instantiate my application when I have the component which is nested under the root component providing a component which it uses in it's constructor.

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


@Component()
export class GrandChild(){
  constructor () {
    this.hello = "hey!"
  }
}

@Component({
  providers: [GrandChild]
})
@Injectable()
export class Child {
    constructor(public grandchild: GrandChild) {
        this.grandchild = grandchild;
    }
}

@Component({
    providers: [Child],
    template: `Everything rendered fine!`,
    selector: 'app'
})

export class App {
    kid: Child;

    constructor(public child: Child) {
        this.kid = child;
    }
}

EXCEPTION: No provider for GrandChild! (App -> Child -> GrandChild)

I'm wondering why this behavior is illegal. Let's say I wanted to use the GrandChild class in my Child class and simply reference the Child class in my App class. This seems to be impossible.

What is the correct way to create the provide hierarchy?

Thanks a bunch.

PLNKR: http://plnkr.co/edit/5Z0QMAEyZNUAotZ6r7Yi?p=preview

nikk wong
  • 8,059
  • 6
  • 51
  • 68

2 Answers2

1

You can inject directly the parent component into a component without specifying it into component providers. To do that you need to use component into other ones and set it into the directives attribute:

@Component({
  selector: 'child',
  template: `
    <div></div>
  `
})
export class Child {
  constructor(@Inject(forwardRef(() => App)) parent: App) {
  }
}

@Component({
  directives: [Child],
  template: `
    Everything rendered fine!
    <child></child>
  `,
  selector: 'app'
})
export class App {
  constructor() {
  }
}

What is a bit strange in your sample is that you didn't define selector and template properties for components Child and GrandChild. Are they really components?

In your sample, you made things in the wrong way. If you want to get instances of child component from a parent component, you need to leverage the @ViewChild decorator to reference the child component from the parent one by injection:

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

(...)

@Component({
  selector: 'my-app',
  template: `
    <h1>My First Angular 2 App</h1>
    <child></child>
    <button (click)="submit()">Submit</button>
  `,
  directives:[App]
})
export class AppComponent { 
  @ViewChild(SearchBar) searchBar:SearchBar;

  (...)

  someOtherMethod() {
    this.searchBar.someMethod();
  }
}

Here is the updated plunkr: http://plnkr.co/edit/mrVK2j3hJQ04n8vlXLXt?p=preview.

You can notice that the @Query parameter decorator could also be used:

export class AppComponent { 
  constructor(@Query(SearchBar) children:QueryList<SearchBar>) {
    this.childcmp = children.first();
  }

  (...)
}

You can notice that you don't need the @Injectable decorator when you have the @Component one...

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
1

You can fix it by adding GrandChild to App's providers.

However, I don't understand why you were annotating Child and GrandChild as @Component when you're using them as services. I guess this is what you actually wanted to do.

@Injectable()
export class GrandChild(){
  constructor () {
    this.hello = "hey!"
  }
}

@Injectable()
export class Child {
    tire: string;
    constructor(public grandchild: GrandChild) {
        this.grandchild = grandchild;
    }
}

@Component({
    providers: [Child, GrandChild],
    template: `Everything rendered fine!`,
    selector: 'app'
})
export class App {
    thread: Thread;
    selected: boolean = false;
    kid: Child;

    constructor(public child: Child) {
        this.kid = child;
    }
}

See updated plnkr demo.

martin
  • 93,354
  • 25
  • 191
  • 226
  • I'm going to assume this means that the highest level components that needs an instance of a service must either ```provide``` it to itself through component metadata or conversely must inherit that service from a parent component. Would this be correct? – nikk wong Feb 18 '16 at 02:58
  • You're right., if this is what you're trying to do. – martin Feb 18 '16 at 07:25