什么是Angular中的ngDefaultControl? [英] What is ngDefaultControl in Angular?

查看:936
本文介绍了什么是Angular中的ngDefaultControl?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不,这不是一个重复的问题。你看,在SO和Github中有很多问题和问题规定我将这个指令添加到具有 [(ngModel)] 指令的标签中,并且不包含在一种形式。如果我不添加它我会收到一个错误:

 错误错误:没有带有未指定名称属性的表单控件的值访问器

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



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



然后。关于 ngModel

$的部分,我在Angular 中找不到关于表单的两个巨大指南b
$ b



  • 经过一番调查,我们发现三个指令将应用于我们的输入



    1) NgControlStatus

      @Directive({
    selector:'[formControlName],[ngModel],[formControl]',
    ...
    })
    导出类NgControlStatus扩展AbstractControlStatus {
    ...
    }

    2) NgModel

      @Directive({
    selector:'[ngModel]:not([formControlName]): not([formControl])',
    providers:[formControlBinding],
    exportAs:'ngModel'
    })
    导出类NgModel扩展NgControl实现OnChanges,

    3) DEFAULT_VALUE_ACCESSOR

      @Directive({
    selector:
    `input:not([type = checkbox])[formControlName],
    textarea [ formControlName],
    输入:not([type = checkbox])formControl],
    textarea [formControl],
    输入:not([type = checkbox])[ngModel],
    textarea [ngModel],[ngDefaultControl]',
    ,,,
    })
    导出类DefaultValueAccessor实现ControlValueAccessor {

    NgControlStatus 指令只是操作类 ng-valid ng-touching ng-dirty 我们可以在这里省略它。






    DefaultValueAccesstor 在providers数组中提供 NG_VALUE_ACCESSOR 标记:

      export const DEFAULT_VALUE_ACCESSOR:any = {
    提供:NG_VALUE_ACCESSOR,
    useExisting:forwardRef(()=> DefaultValueAccessor),
    multi:true
    };
    ...
    @Directive({
    ...
    providers:[DEFAULT_VALUE_ACCESSOR]
    })
    导出类DefaultValueAccessor实现ControlValueAccessor {

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

      export NgModel extends NgControl实现OnChanges,OnDestroy {
    构造函数(...
    @Optional()@ Self()@ Inject(NG_VALUE_ACCESSOR)valueAccessors:ControlValueAccessor []){

    在我们的例子中, NgModel 将注入 DefaultValueAccessor 。现在NgModel指令调用共享 setUpControl 函数:

      export function setUpControl(control:FormControl,dir:NgControl):void {
    if(!control)_throwError(dir,'找不到控件');
    if(!dir.valueAccessor)_throwError(dir,'没有表单控件的值访问器');

    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);

    ...
    }

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

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

    函数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)属性中存储回调并在<$ c时触发此回调$ c>输入事件发生(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});
    }

    其中angular调用表格API control.setValue



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


    No, this is not a duplicate question. You see, there is a ton of questions and issues in SO and Github that prescribe that I add this directive to a tag that has [(ngModel)] directive and is not contained in a form. If I don't add it I get an error:

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

    Ok, error goes away if I put this attribute there. BUT, wait! Nobody knows what it does! And Angular's doc doesn't mention it at all. Why do I need a value accessor when I know that I don't need it? How is this attribute connected to value accessors? What does this directive do? What is a value acessor and how do I use it?

    And why does everybody keep doing things that they don't understand t all? Just add this line of code and it works, thank you, this is not the way to write good programs.

    And then. I read not one but two huge guides about forms in Angular and a section about ngModel:

    And you know what? Not a single mention of either value accessors or ngDefaultControl. Where is it?

    解决方案

    [ngDefaultControl]

    Third party controls require a ControlValueAccessor to function with angular forms. Many of them, like Polymer's <paper-input>, behave like the <input> native element and thus can use the DefaultValueAccessor. Adding an ngDefaultControl attribute will allow them to use that directive.

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

    or

    <paper-input ngDefaultControl formControlName="name">
    

    So this is the main reason why this attrubute was introduced.

    It was called ng-default-control attribute in alpha versions of angular2.

    So ngDefaultControl is one of selectors for DefaultValueAccessor directive:

    @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 {
    

    What does it mean?

    It means that we can apply this attribute to element(like polymer component) that doesn't have its own value accessor. So this element will take behaviour from DefaultValueAccessor and we can use this element with angular forms.

    Otherwise you have to provide your own implementation of ControlValueAccessor

    ControlValueAccessor

    Angular docs states

    A ControlValueAccessor acts as a bridge between the Angular forms API and a native element in the DOM.

    Let's write the following template in simple angular2 application:

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

    To understand how our input above will behave we need to know which directives are applied to this element. Here angular gives out some hint with the error:

    Unhandled Promise rejection: Template parse errors: Can't bind to 'ngModel' since it isn't a known property of 'input'.

    Okay, we can open SO and get the answer: import FormsModule to your @NgModule:

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

    We imported it and all works as intended. But what's going on under the hood?

    FormsModule exports for us the following directives:

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

    After some investigation we can discover that three directives will be applied to our 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 directive just manipulates classes like ng-valid, ng-touched, ng-dirty and we can omit it here.


    DefaultValueAccesstor provides NG_VALUE_ACCESSOR token in providers array:

    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 directive injects in constructor NG_VALUE_ACCESSOR token that was declared on the same host element.

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

    In our case NgModel will inject DefaultValueAccessor. And now NgModel directive calls shared setUpControl function:

    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);
      });
    }
    

    And here is the bridge in action:

    NgModel sets up control (1) and calls dir.valueAccessor !.registerOnChange method. ControlValueAccessor stores callback in onChange(2) property and fires this callback when input event happens (3). And finally updateControl function is called inside callback (4)

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

    where angular calls forms API control.setValue.

    That's a short version of how it works.

    这篇关于什么是Angular中的ngDefaultControl?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆