【问题标题】:What is ngDefaultControl in Angular?Angular 中的 ngDefaultControl 是什么?
【发布时间】:2018-03-10 00:07:32
【问题描述】:

不,这不是重复的问题。您会看到,SO 和 Github 中有大量问题和问题,规定我将此指令添加到具有 [(ngModel)] 指令且不包含在表单中的标签中。如果我不添加它,我会收到错误:

ERROR Error: No value accessor for form control with unspecified name attribute

好的,如果我把这个属性放在那里,错误就会消失。可是等等!没有人知道它的作用! Angular的文档根本没有提到它。当我知道我不需要值访问器时,为什么我需要它?该属性如何与值访问器相关联?这个指令有什么作用?什么是值访问器以及如何使用它?

为什么每个人都继续做他们根本不了解的事情?加上这行代码就可以了,谢谢,这不是写好程序的方法。

然后。我阅读的不是一个,而是 两个关于 Angular 表单的大量指南一个关于 ngModel 的部分:

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

【问题讨论】:

  • > “为什么每个人都继续做他们根本不理解的事情?” - 是的!确切地!不过,它可以使用更多的感叹号 ;-)

标签: javascript angular


【解决方案1】:

[ngDefaultControl]

第三方控件需要ControlValueAccessor 才能使用角度形式。它们中的许多,如 Polymer 的 <paper-input>,行为类似于 <input> 原生元素,因此可以使用 DefaultValueAccessor。添加ngDefaultControl 属性将允许他们使用该指令。

<paper-input ngDefaultControl [(ngModel)]="value>

<paper-input ngDefaultControl formControlName="name">

所以这就是引入这个属性的主要原因。

它被称为ng-default-control 属性in alpha versions of angular2

所以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 {

这是什么意思?

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

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

ControlValueAccessor

docs states

ControlValueAccessor 充当 Angular 表单 API 之间的桥梁 以及 DOM 中的原生元素。

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

<input type="text" [(ngModel)]="userName">

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

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

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

@NgModule({
  imports: [
    ...,
    FormsModule
  ]
})
export AppModule {}

我们导入了它,所有作品都按预期工作。但是幕后发生了什么?

FormsModule 为我们导出以下指令:

@NgModule({
 ...
  exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}

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

1) NgControlStatus

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

2) NgModel

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

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 {

NgControlStatus 指令只操作ng-validng-touchedng-dirty 等类,我们可以在此处省略它。


DefaultValueAccesstor 在 providers 数组中提供 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 {

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

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

在我们的例子中,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);
  });
}

这是正在运行的桥梁:

NgModel 设置控制(1) 并调用dir.valueAccessor !.registerOnChange 方法。 ControlValueAccessor 将回调存储在 onChange(2) 属性中,并在 input 事件发生时触发此回调(3)。最后在回调(4)

中调用updateControl函数
function updateControl(control: FormControl, dir: NgControl): void {
  dir.viewToModelUpdate(control._pendingValue);
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, {emitModelToViewChange: false});
}

其中 Angular 调用形成 API control.setValue

这是其工作原理的简短版本。

【讨论】:

  • 我刚刚制作了@Input() ngModel@Output() ngModelChange 用于双向绑定,我认为它应该足够了。这看起来像是以完全不同的方式做同样的事情。也许我不应该将我的字段命名为ngModel
  • 如果您不将此组件与角度形式一起使用,那么您可以创建自己的双向绑定,如@Input() value; @Output() valueChange: EventEmitter&lt;any&gt; = new EventEmitter();,然后使用[(value)]="someProp"
  • 这正是我所做的。但我将我的“价值”命名为ngModel,Angular 开始向我抛出一个错误并要求使用 ControlValueAccessor。
  • 有谁在 vue 和 React 中相当于 ngDefaultControl 吗?我的意思是,我使用控制值访问器在 Angular 中制作了一个自定义输入组件,并将其包装为 Angular 元素中的 Web 组件。在同一个项目中,我不得不使用 ngDefaultControl 来使其与角度形式一起工作。但是我应该怎么做才能让它们在 Vue 和 React 中工作?也在原生 JS 中?
  • 我在我的自定义组件上使用 ngDefaultControl,但我遇到了一个问题。当我在我的 formBuilder 视图(自定义输入组件)中为 formControl 设置默认值时,不会更新,只有模型。我做错了什么?
猜你喜欢
  • 1970-01-01
  • 2017-12-08
  • 2023-01-06
  • 2018-08-13
  • 2017-02-06
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多