什么是Angular中的ngDefaultControl?

Ghe*_*man 77 javascript angular

不,这不是一个重复的问题.你看,在SO和Github中有很多问题和问题规定我将这个指令添加到一个带有[(ngModel)]指令但不包含在表单中的标签中.如果我不添加它我会收到一个错误:

ERROR Error: No value accessor for form control with unspecified name attribute
Run Code Online (Sandbox Code Playgroud)

好的,如果我把这个属性放在那里,错误就消失了.可是等等!没人知道它的作用!Angular的文档根本没有提到它.当我知道我不需要它时,为什么需要值访问器?该属性如何连接到值访问器?这个指令做了什么?什么是价值追求者以及如何使用它?

为什么每个人都继续做他们不理解的事情?只需添加这行代码即可,谢谢,这不是编写好程序的方法.

然后.我在Angular上阅读了关于表单的两个大型指南,以及关于以下内容的部分ngModel:

你知道吗?没有提到任何值访问器或ngDefaultControl.它在哪里?

yur*_*zui 130

[ngDefaultControl]

第三方控件需要ControlValueAccessor使用角形式来运行.其中许多像Polymer一样<paper-input>,表现得像<input>原生元素,因此可以使用DefaultValueAccessor.添加ngDefaultControl属性将允许他们使用该指令.

<paper-input ngDefaultControl [(ngModel)]="value>
Run Code Online (Sandbox Code Playgroud)

要么

<paper-input ngDefaultControl formControlName="name">
Run Code Online (Sandbox Code Playgroud)

所以这就是为什么引入这种信息的主要原因.

在angular2的alpha版本中被称为ng-default-control属性.

因此ngDefaultControlDefaultValueAccessor指令的选择器之一:

@Directive({
  selector:
      'input:not([type=checkbox])[formControlName],
       textarea[formControlName],
       input:not([type=checkbox])[formControl],
       textarea[formControl],
       input:not([type=checkbox])[ngModel],
       textarea[ngModel],
       [ngDefaultControl]', <------------------------------- this selector
  ...
})
export class DefaultValueAccessor implements ControlValueAccessor {
Run Code Online (Sandbox Code Playgroud)

这是什么意思?

这意味着我们可以将此属性应用于没有自己的值访问器的元素(如聚合物组件).所以这个元素将采取行为DefaultValueAccessor,我们可以使用这个元素与角形式.

否则你必须提供自己的实现 ControlValueAccessor

ControlValueAccessor

Angular docs声明

ControlValueAccessor充当Angular表单API和DOM中的本机元素之间的桥梁.

让我们在简单的angular2应用程序中编写以下模板:

<input type="text" [(ngModel)]="userName">
Run Code Online (Sandbox Code Playgroud)

要了解我们的input上述行为,我们需要知道哪些指令应用于此元素.这里angular给出了一些错误提示:

未处理的Promise拒绝:模板解析错误:无法绑定到'ngModel',因为它不是'input'的已知属性.

好的,我们可以打开SO并得到答案:导入FormsModule到您的@NgModule:

@NgModule({
  imports: [
    ...,
    FormsModule
  ]
})
export AppModule {}
Run Code Online (Sandbox Code Playgroud)

我们导入了它,并且一切按预期工作.但是引擎盖下发生了什么?

FormsModule为我们导出以下指令:

@NgModule({
 ...
  exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

经过一番调查,我们发现三个指令将适用于我们的 input

1)NgControlStatus

@Directive({
  selector: '[formControlName],[ngModel],[formControl]',
  ...
})
export class NgControlStatus extends AbstractControlStatus {
  ...
}
Run Code Online (Sandbox Code Playgroud)

2)NgModel

@Directive({
  selector: '[ngModel]:not([formControlName]):not([formControl])',
  providers: [formControlBinding],
  exportAs: 'ngModel'
})
export class NgModel extends NgControl implements OnChanges, 
Run Code Online (Sandbox Code Playgroud)

3)DEFAULT_VALUE_ACCESSOR

@Directive({
  selector:
      `input:not([type=checkbox])[formControlName],
       textarea[formControlName],
       input:not([type=checkbox])formControl],
       textarea[formControl],
       input:not([type=checkbox])[ngModel],
       textarea[ngModel],[ngDefaultControl]',
  ,,,
})
export class DefaultValueAccessor implements ControlValueAccessor {
Run Code Online (Sandbox Code Playgroud)

NgControlStatus指令只是操纵类,如ng-valid,ng-touched,ng-dirty我们可以在这里省略.


DefaultValueAccesstor提供NG_VALUE_ACCESSOR者数组中提供令牌:

export const DEFAULT_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DefaultValueAccessor),
  multi: true
};
...
@Directive({
  ...
  providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {
Run Code Online (Sandbox Code Playgroud)

NgModel指令注入在NG_VALUE_ACCESSOR同一主机元素上声明的构造函数标记.

export NgModel extends NgControl implements OnChanges, OnDestroy {
 constructor(...
  @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
Run Code Online (Sandbox Code Playgroud)

在我们的情况下NgModel将注入DefaultValueAccessor.现在NgModel指令调用共享setUpControl函数:

export function setUpControl(control: FormControl, dir: NgControl): void {
  if (!control) _throwError(dir, 'Cannot find control with');
  if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');

  control.validator = Validators.compose([control.validator !, dir.validator]);
  control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]);
  dir.valueAccessor !.writeValue(control.value);

  setUpViewChangePipeline(control, dir);
  setUpModelChangePipeline(control, dir);

  ...
}

function setUpViewChangePipeline(control: FormControl, dir: NgControl): void 
{
  dir.valueAccessor !.registerOnChange((newValue: any) => {
    control._pendingValue = newValue;
    control._pendingDirty = true;

    if (control.updateOn === 'change') updateControl(control, dir);
  });
}

function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
  control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor !.writeValue(newValue);

    // control -> ngModel
    if (emitModelEvent) dir.viewToModelUpdate(newValue);
  });
}
Run Code Online (Sandbox Code Playgroud)

以下是行动中的桥梁:

在此输入图像描述

NgModel设置控件(1)和调用dir.valueAccessor !.registerOnChange方法.ControlValueAccessoronChange(2)属性中存储回调并在input事件发生时触发此回调(3).最后updateControl函数在回调内调用(4)

function updateControl(control: FormControl, dir: NgControl): void {
  dir.viewToModelUpdate(control._pendingValue);
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, {emitModelToViewChange: false});
}
Run Code Online (Sandbox Code Playgroud)

其中angular调用形成API control.setValue.

这是它的工作原理的简短版本.

  • 如果您不将此组件与角度形式一起使用,那么您可以创建自己的双向绑定,例如 `@Input() 值;@Output() valueChange: EventEmitter&lt;any&gt; = new EventEmitter();` 然后只需使用 `[(value)]="someProp"` (4认同)
  • 它已经保留了名称)选择别的东西 (3认同)
  • 这正是我正在做的。但是我将我的“值”命名为 `ngModel`,Angular 开始向我抛出错误并要求使用 ControlValueAccessor。 (2认同)