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.