- import {Directive} from '@angular/core';
- @Directive({
- selector: '[ngbButtonLabel]',host:
- {'[class.btn]': 'true','[class.active]': 'active','[class.disabled]': 'disabled','[class.focus]': 'focused'}
- })
- export class NgbButtonLabel {
- active: boolean;
- disabled: boolean;
- focused: boolean;
- }
这是单选按钮代码:
- import {Directive,forwardRef,Input,Renderer2,ElementRef,OnDestroy} from '@angular/core';
- import {ControlValueAccessor,NG_VALUE_ACCESSOR} from '@angular/forms';
- import {NgbButtonLabel} from './label';
- const NGB_RAdio_VALUE_ACCESSOR = {
- provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => NgbRadioGroup),multi: true
- };
- let nextId = 0;
- /**
- * Easily create Bootstrap-style radio buttons. A value of a selected button is bound to a variable
- * specified via ngModel.
- */
- @Directive({
- selector: '[ngbRadioGroup]',host: {'data-toggle': 'buttons','role': 'group'},providers: [NGB_RAdio_VALUE_ACCESSOR]
- })
- export class NgbRadioGroup implements ControlValueAccessor {
- private _radios: Set<NgbRadio> = new Set<NgbRadio>();
- private _value = null;
- private _disabled: boolean;
- get disabled() { return this._disabled; }
- set disabled(isDisabled: boolean) { this.setDisabledState(isDisabled); }
- /**
- * The name of the group. Unless enclosed inputs specify a name,this name is used as the name of the
- * enclosed inputs. If not specified,a name is generated automatically.
- */
- @Input() name = `ngb-radio-${nextId++}`;
- onChange = (_: any) => {};
- onTouched = () => {};
- onRadioChange(radio: NgbRadio) {
- this.writeValue(radio.value);
- this.onChange(radio.value);
- }
- onRadioValueUpdate() { this._updateRadiosValue(); }
- register(radio: NgbRadio) { this._radios.add(radio); }
- registerOnChange(fn: (value: any) => any): void { this.onChange = fn; }
- registerOnTouched(fn: () => any): void { this.onTouched = fn; }
- setDisabledState(isDisabled: boolean): void {
- this._disabled = isDisabled;
- this._updateRadiosDisabled();
- }
- unregister(radio: NgbRadio) { this._radios.delete(radio); }
- writeValue(value) {
- this._value = value;
- this._updateRadiosValue();
- }
- private _updateRadiosValue() { this._radios.forEach((radio) => radio.updateValue(this._value)); }
- private _updateRadiosDisabled() { this._radios.forEach((radio) => radio.updateDisabled()); }
- }
- /**
- * Marks an input of type "radio" as part of the NgbRadioGroup.
- */
- @Directive({
- selector: '[ngbButton][type=radio]',host: {
- '[checked]': 'checked','[disabled]': 'disabled','[name]': 'nameAttr','(change)': 'onChange()','(focus)': 'focused = true','(blur)': 'focused = false'
- }
- })
- export class NgbRadio implements OnDestroy {
- private _checked: boolean;
- private _disabled: boolean;
- private _value: any = null;
- /**
- * The name of the input. All inputs of a group should have the same name. If not specified,* the name of the enclosing group is used.
- */
- @Input() name: string;
- /**
- * You can specify model value of a given radio by binding to the value property.
- */
- @Input('value')
- set value(value: any) {
- this._value = value;
- const stringValue = value ? value.toString() : '';
- this._renderer.setProperty(this._element.nativeElement,'value',stringValue);
- this._group.onRadioValueUpdate();
- }
- /**
- * A flag indicating if a given radio button is disabled.
- */
- @Input('disabled')
- set disabled(isDisabled: boolean) {
- this._disabled = isDisabled !== false;
- this.updateDisabled();
- }
- set focused(isFocused: boolean) {
- if (this._label) {
- this._label.focused = isFocused;
- }
- }
- get checked() { return this._checked; }
- get disabled() { return this._group.disabled || this._disabled; }
- get value() { return this._value; }
- get nameAttr() { return this.name || this._group.name; }
- constructor(
- private _group: NgbRadioGroup,private _label: NgbButtonLabel,private _renderer: Renderer2,private _element: ElementRef) {
- this._group.register(this);
- }
- ngOnDestroy() { this._group.unregister(this); }
- onChange() { this._group.onRadioChange(this); }
- updateValue(value) {
- this._checked = this.value === value;
- this._label.active = this._checked;
- }
- updateDisabled() { this._label.disabled = this.disabled; }
- }
请注意
- @Directive({
- selector: '[ngbButton][type=radio]','(blur)': 'focused = false'
- }
- })
没有provider部分,但构造函数有一个NgbRadioGroup和NgbButtonLabel.此外,在使用指令时,不要像这样放弃ngbButtonLabel:
导致NgbButtonLabel没有提供者!错误.我遗失了什么声明?以下是其完整存储库的链接:https://github.com/ng-bootstrap/ng-bootstrap
- <input ngbButton type="radio" ...>
在您提供NgbRadio指令的情况下,将提供您提供NgbButtonLabel指令的父元素.
所以你的模板应该是这样的:
要理解为什么会这样,你需要从层次结构树中知道how angular gets dependencies.
假设我们的根组件中有以下模板:
app.component.html
- <div dirA>
- <comp-b dirB>
- <span dirC>
- <i dirD></i>
- </span>
- </comp-b>
- </div>
以及以下指令集:
- @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) {}
- }
注意:私有dirB:DirB就像私有_label:NgbButtonLabel在你的情况下
Angular编译器为我们的模板创建视图工厂:
注意:我在组件上使用了新的preserveWhitespaces:false选项,因此我们在工厂中看不到textDef.
然后从这个工厂角度creates ViewDefinition并实例化主机元素的提供者.
角度编译器在哪里采用提供者?
你应该知道的主要事情是each directive provides its own token:
所以这里的提供者看起来如下:
- <div dirA> [DirA]
- <comp-b dirB> [ComponentB,DirB]
- <span dirC> [DirC]
- <i dirD></i> [DirD]
- </span>
- </comp-b>
- </div>
以下规则是我们在指令元数据中声明的提供程序(提供程序数组)也将添加到主机元素提供程序:
- <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>
现在angular正试图获得DirB指令的提供者
- @Directive({ selector: 'dirD' })
- export class DirD {
- constructor(private dirB: DirB) {}
- }
角度依赖性解析机制以< i dirD>< / i>开头.节点并上升到< div dirA>:
- null or throw error
- /\
- @NgModule
- /\
- my-app
- <div dirA> /\ [DirA,useValue: 'dirA provider' }]
- <comp-b dirB> /\ [ComponentB,useValue: 'comp-b provider'}]
- <span dirC> /\ [DirC]
- <i dirD></i> /\ [DirD]
- </span>
- </comp-b>
- </div>
因此,角度将在< comp-b dirB>上找到DirB提供者.主机元素.我们可能会认为angular会使DirB提供者BUT三步
确实是角度uses prototypical inheritance来定义元素的提供者.
这样我们的树看起来像:
- null or throw error
- /\
- @NgModule
- /\
- my-app
- <div dirA> /\ [
- DirA,useValue: 'dirA provider' }
- ]
- <comp-b dirB> /\ [
- ComponentB,useValue: 'comp-b provider'},DirA,useValue: 'dirA provider' }
- ]
- <span dirC> /\ [
- DirC,ComponentB,useValue: 'dirA provider' }
- ]
- <i dirD></i> /\ [
- DirD,DirC,useValue: 'dirA provider' }
- ]
- </span>
- </comp-b>
- </div>
正如我们所看到的,实际上角度仅使用一步来从< i dirD>< / i>中找到DirB提供者.主机元素.