首页 文章

Angular 2 - 当html字段具有formControlName时,在custom指令中注入ngModel

提问于
浏览
0

我尝试在我创建的指令(带有@Directive的类)提供的字段中使用自定义属性,并且此指令在构造函数中注入了NgForm,但如果该字段具有formControlName属性,则它不起作用 .

这是演示的插件:http://plnkr.co/edit/gdm3Xb?p=preview

情况

该字段与ngModel绑定并具有双向绑定,因为我使用该模型在我想要(或需要)或将数据发送到服务器时直接更新字段 . 在提交表单的过程中,我不使用表单本身的字段(带控件),因为模型类有几种方法,不仅仅是一个贫血模型,而是一个具有多种功能的丰富模型,而我不是必须关心像控件这样的低级东西 . 该模型还包含所有字段(如果需要,甚至计算字段,不在演示中) . 所以我已经包含了[(ngModel)]属性 .

但另一方面,我使用FormControls来使用角度验证,所以我包含了一个formControlName属性,验证工作正常,字段的行为符合预期 .

到目前为止它还可以,但是当我创建一个指令(带有@Directive的类)作为属性(在这种情况下,属性myDirective)在具有formControlName属性的字段中使用并且指令注入了NgModel时,我收到了以下错误:

Error: Uncaught (in promise): EXCEPTION: Error in ./AppComponent class AppComponent - inline template:4:3
ORIGINAL EXCEPTION: No provider for NgModel!

在演示中,我使用@Optional for NgModel以便不接收错误和页面加载,但是我记录了ngModel并且它显示它是null .

如果我在提供程序中显式提供NgModel它没有显示错误,但它不是我想要的ngModel(与所讨论的控件/字段无关),所以如果我尝试将更改应用于该ngModel它不会反映在该领域 .

相反,如果我删除了formControlName,该指令工作正常(你可以在日志中看到我在一行中记录'ngModel:'和下一个中的对象:ngModel之前是null,现在是NgModel类型的对象) . 由于name属性(formBuilder根据字段的'name'属性绑定),也会发生验证 . 问题是模型中的初始值没有显示在字段中,只显示在该值更改后(您可以在下面显示我将模型对象显示为JSON的字段),并且该字段不会保留在即使其中的值无效,也就是无效状态,例如当我擦除内容时(边框不会变为红色并且在浏览器中检查该类是非有效的,并且即使在更改之后它也是未触及的) ,有点像场和控制没有连接) .


档案

my.directive.ts

import { Directive, Optional } from '@angular/core';
import { NgModel } from '@angular/forms';

@Directive({
    selector: '[myDirective]'
})
export class MyDirective {
    constructor(@Optional() ngModel: NgModel) {
        console.log('ngModel:');
        console.log(ngModel);       
    }
}

app.component.ts

import { Component } from '@angular/core';
import { REACTIVE_FORM_DIRECTIVES, FormBuilder, FormGroup, Validators } from '@angular/forms';

import { MyDirective } from './my.directive';

@Component({
    selector: 'my-app',
    template: `
        <h1>My Angular 2 App</h1>

        <form [formGroup]="formGroup" [class.error]="!myInput.valid && myInput.touched">
            <input 
                name="myInput" 
                type="text" 
                [(ngModel)]="model.myInput" 
                formControlName="myInput" 
                #myInput
                myDirective="123"
            >
            <div *ngIf="formGroup.controls.myInput.dirty && !formGroup.controls.myInput.valid">
                myInput is required
            </div>
        </form>
    `,
    directives: [REACTIVE_FORM_DIRECTIVES, MyDirective]
})
export class AppComponent {
    public model: { myInput: number } = { myInput: 456 };
    public formGroup: FormGroup;

    constructor(private formBuilder: FormBuilder) { }

    ngOnInit() {
        this.formGroup = this.formBuilder.group({ myInput: ['', [Validators.required], []] });
    }
}

main.ts

import { bootstrap } from '@angular/platform-browser-dynamic';
import { disableDeprecatedForms, provideForms } from '@angular/forms';
import { AppComponent } from './app.component';

bootstrap(AppComponent, [
    disableDeprecatedForms(),
    provideForms()
]);

TL; DR

I would like to know if there is a way to make the directive receive NgModel, but at the same time I want that the form also works the way that is doing now (with two-way data binding so I can use the model object, but also use the FormBuilder to define the validators for the fields).

更新

只是为了澄清,我想要的是与控件相关的angular2对象NgModel,使用viewToModelUpdate等方法和valueAccessor等属性 . 如果删除formControlName属性,则可以看到我的plunker中记录的NgModel对象 .

1 回答

  • 0

    除了 @Optional() arg如何工作之外我找不到问题,所以我试图找到另一种方法来做,看看它是否为你解决了问题!


    档案

    my.directive.ts

    import { Directive, Input,Optional , HostListener} from '@angular/core';
    import { NgModel } from '@angular/forms';
    
    @Directive({
        selector: '[myDirective]'
    })
    export class MyDirective {
        @Input('myDirective') ngModel:NgModel;
        constructor() {
        }
        /**Added to check if the Input is being updated**/
        @HostListener('mouseenter') onMouseEnter() {
          console.log('ngModel:');
          console.log(this.ngModel)
        }
        /**Inputs are only received OnInit**/
        ngOnInit()
        {
            console.log('ngModel:');
            console.log(this.ngModel)
        }
    }
    

    app.component.ts

    import { Component } from '@angular/core';
    import { REACTIVE_FORM_DIRECTIVES, FormBuilder, FormGroup, Validators } from '@angular/forms';
    
    import { MyDirective } from './my.directive';
    
    @Component({
        selector: 'my-app',
        template: `
            <h1>My Angular 2 App</h1>
    
            <form [formGroup]="formGroup">
                <input 
                    name="myInput" 
                    type="text" 
                    [(ngModel)]="model.myInput" 
                    formControlName="myInput"
                    [myDirective]="model"
                    #myInput
                >
                <div *ngIf="formGroup.controls.myInput.dirty && !formGroup.controls.myInput.valid">
                    myInput is required
                </div>
    
                <br><br>
    
                {{ model | json }}
            </form>
        `,
        directives: [REACTIVE_FORM_DIRECTIVES, MyDirective]
    })
    export class AppComponent {
        public model: { myInput: number } = { myInput: 456 };
        public formGroup: FormGroup;
    
        constructor(private formBuilder: FormBuilder) { }
    
        ngOnInit() {
            this.formGroup = this.formBuilder.group({ myInput: ['789', [Validators.required], []] });
        }
    }
    

    工作人员:http://plnkr.co/edit/nYtsXH?p=preview

相关问题