Angular 2 - How does ng-bootstrap provide the NgbRadioGroup and NgbButtonLabel to their NgbRadio directive?

落花浮王杯 提交于 2019-11-29 04:53:31

ng-bootstrap package expects that the element

<input ngbButton type="radio" ...>

, on which you provided NgbRadio directive, will have parent element on which you provided NgbButtonLabel directive.

So your template should looks like:

<label ngbButtonLabel> <======== add ngbButtonLabel attribute
  <input ngbButton type="radio" name="radio" [value]="values[0]"/> {{ values[0] }}
</label>

To understand why this is so you need to know how angular gets dependencies from hierarchical tree of elements.

Let's say we have the following template in our root component:

app.component.html

<div dirA>
  <comp-b dirB>
    <span dirC>
      <i dirD></i>
    </span>
  </comp-b>
</div>

and the following set of directives:

@Directive({
  selector: '[dirA]',
  providers: [{ provide: 'A', useValue: 'dirA provider' }]
})
export class DirA {}

@Component({
  selector: 'comp-b',
  template: '<ng-content></ng-content>',
  providers: [{ provide: 'B', useValue: 'comp-b provider'}]
})
export class ComponentB {}

@Directive({ selector: 'dirB' })
export class DirB {}

@Directive({ selector: 'dirC' })
export class DirC {}

@Directive({ selector: 'dirD' })
export class DirD {
  constructor(private dirB: DirB) {}
}

Note: private dirB: DirB is like private _label: NgbButtonLabel in your case

Angular compiler creates view factory for our template:

Note: i used new preserveWhitespaces: false option on component so we don't see textDef in the factory.

Then angular creates ViewDefinition from this factory and also instantiates providers for host elements.

Where does angular compiler take providers?

Main thing you should know is that each directive provides its own token:

So providers here could look as follows:

<div dirA>               [DirA]
  <comp-b dirB>          [ComponentB, DirB]
    <span dirC>          [DirC] 
      <i dirD></i>       [DirD]
    </span>
  </comp-b>
</div>

The following rule is providers that we are declaring within directive metadata(providers array) will also be added to host element providers:

<div dirA>               [DirA, { provide: 'A', useValue: 'dirA provider' }]
  <comp-b dirB>          [ComponentB, DirB, { provide: 'B', useValue: 'comp-b provider'}]
    <span dirC>          [DirC] 
      <i dirD></i>       [DirD]
    </span>
  </comp-b>
</div>

Now angular is trying to get provider for DirB directive

@Directive({ selector: 'dirD' })
export class DirD {
  constructor(private dirB: DirB) {}
}

Angular dependency resolution mechanism starts with <i dirD></i> node and goes up to <div dirA>:

              null or throw error
                    /\
                 @NgModule
                    /\
                  my-app
<div dirA>          /\     [DirA, { provide: 'A', useValue: 'dirA provider' }]
  <comp-b dirB>     /\     [ComponentB, DirB, { provide: 'B', useValue: 'comp-b provider'}]
    <span dirC>     /\     [DirC]   
      <i dirD></i>  /\     [DirD]  
    </span>
  </comp-b>
</div>

So angular will find DirB provider on <comp-b dirB> host element. We might think that angular will make three steps up to get DirB provider BUT Indeed angular uses prototypical inheritance to define providers on elements.

This way our tree will look like:

              null or throw error
                    /\
                 @NgModule
                    /\
                  my-app
<div dirA>          /\     [
                             DirA, { provide: 'A', useValue: 'dirA provider' }
                           ]
  <comp-b dirB>     /\     [
                             ComponentB, 
                             DirB, { provide: 'B', useValue: 'comp-b provider'}, 
                             DirA, { provide: 'A', useValue: 'dirA provider' }
                           ]
    <span dirC>     /\     [
                             DirC, ComponentB, 
                             DirB, { provide: 'B', useValue: 'comp-b provider'}, 
                             DirA, { provide: 'A', useValue: 'dirA provider' }
                           ]  
      <i dirD></i>  /\     [
                             DirD, DirC, ComponentB, 
                             DirB, { provide: 'B', useValue: 'comp-b provider'}, 
                             DirA, { provide: 'A', useValue: 'dirA provider' }
                           ]  
    </span>
  </comp-b>
</div>

As we can see actually angular uses only one step to find DirB provider from <i dirD></i> host element.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!