Angular 2 - 当 html 字段具有 formControlName 时,在自定义指令中注入 ngModel [英] Angular 2 - Inject ngModel in custom directive when html field has formControlName

查看:29
本文介绍了Angular 2 - 当 html 字段具有 formControlName 时,在自定义指令中注入 ngModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是演示的 plunker:http://plnkr.co/edit/gdm3Xb?p=预览

情况

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

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

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

Error: Uncaught (in promise): EXCEPTION: Error in ./AppComponent class AppComponent - inline template:4:3原始例外:NgModel 没有提供程序!

在演示中,我对 NgModel 使用了 @Optional,以免收到错误和页面加载,但我记录了 ngModel 并显示它为空.

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

如果相反,我删除了 formControlName,则该指令可以正常工作(您可以在日志中看到,我在一行中记录了ngModel:",在下一行记录了对象:ngModel 之前为空,现在是一个对象NgModel 类型).验证也是因为 name 属性(formBuilder 绑定基于字段的 'name' 属性).问题是模型中的初始值没有显示在字段中,只有在该值更改后才会显示(您可以在我将模型对象显示为 JSON 的字段下方看到),并且该字段也不会留在无效状态,即使其中的值无效,例如当我擦除内容时(边框不会变为红色并在浏览器中检查该类是 ng-valid,并且它也是 ng-untouched,即使在更改后也是如此,有点像字段和控件没有连接).

<小时>

文件

my.directive.ts

import { Directive, Optional } from '@angular/core';从@angular/forms"导入{ NgModel};@指示({选择器:'[myDirective]'})导出类 MyDirective {构造函数(@Optional()ngModel:NgModel){console.log('ngModel:');控制台日志(ngModel);}}

app.component.ts

import { Component } from '@angular/core';import { REACTIVE_FORM_DIRECTIVES, FormBuilder, FormGroup, Validators } from '@angular/forms';从 './my.directive' 导入 { MyDirective };@成分({选择器:'我的应用',模板:`<h1>我的 Angular 2 应用</h1><form [formGroup]="formGroup" [class.error]="!myInput.valid && myInput.touched"><输入名称=我的输入"类型=文本"[(ngModel)]="model.myInput"表单控件名称=我的输入"#我的输入myDirective="123"><div *ngIf="formGroup.controls.myInput.dirty && !formGroup.controls.myInput.valid">我的输入是必需的

</表单>`,指令:[REACTIVE_FORM_DIRECTIVES,MyDirective]})导出类 AppComponent {公共模型:{ myInput: number } = { myInput: 456 };公共表单组:表单组;构造函数(私有表单构建器:FormBuilder){}ngOnInit() {this.formGroup = this.formBuilder.group({ myInput: ['', [Validators.required], []] });}}

ma​​in.ts

import { bootstrap } from '@angular/platform-b​​rowser-dynamic';import { disableDeprecatedForms, provideForms } from '@angular/forms';从 './app.component' 导入 { AppComponent };引导程序(应用程序组件,[disableDeprecatedForms(),提供表单()]);

TL;DR

我想知道是否有办法让指令接收 NgModel,但同时我希望表单也能像现在一样工作(使用双向数据绑定,所以我可以使用模型对象,也可以使用 FormBuilder 来定义字段的验证器).

更新

只是澄清一下,我想要的是与控件相关的 angular2 对象 NgModel,具有像 viewToModelUpdate 这样的方法和像 valueAccessor 这样的属性.如果删除 formControlName 属性,您可以看到 NgModel 对象登录到我的 plunker 中.

解决方案

除了 @Optional()arg 的工作原理之外,我找不到问题,所以我试图找到另一种方法来做看看能不能解决你的问题!

<小时>

文件

my.directive.ts

import { Directive, Input,Optional , HostListener} from '@angular/core';从@angular/forms"导入{ NgModel};@指示({选择器:'[myDirective]'})导出类 MyDirective {@Input('myDirective') ngModel:NgModel;构造函数(){}/**添加以检查输入是否正在更新**/@HostListener('mouseenter') onMouseEnter() {console.log('ngModel:');console.log(this.ngModel)}/**输入仅在 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';从 './my.directive' 导入 { MyDirective };@成分({选择器:'我的应用',模板:`<h1>我的 Angular 2 应用</h1><form [formGroup]="formGroup"><输入名称=我的输入"类型=文本"[(ngModel)]="model.myInput"表单控件名称=我的输入"[myDirective]="模型"#我的输入><div *ngIf="formGroup.controls.myInput.dirty && !formGroup.controls.myInput.valid">我的输入是必需的

<br><br>{{ 模型 |json }}</表单>`,指令:[REACTIVE_FORM_DIRECTIVES,MyDirective]})导出类 AppComponent {公共模型:{ myInput: number } = { myInput: 456 };公共表单组:表单组;构造函数(私有表单构建器:FormBuilder){}ngOnInit() {this.formGroup = this.formBuilder.group({ myInput: ['789', [Validators.required], []] });}}

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

I have tried using a custom attribute in a field provided by a directive (class with @Directive) that I created, and this directive has NgForm injected in the constructor, but it doesn't work if the field has the formControlName attribute.

Here is the plunker of a demo: http://plnkr.co/edit/gdm3Xb?p=preview

Situation

The field is bound with ngModel with two-way bind, because I use the model for updating directly the fields when I want (or need) or sending data to the server. In processes like submiting the form I don't use the fields from the form itself (with controls) because the model class has several methods and isn't just an anemic model, but a rich one with several functionalities, and I don't have to care with low-level stuff like controls. The model also has all the fields (even calculated fields, if needed, not in the demo). So I have included a [(ngModel)] attribute.

But on the other hand, I use FormControls to use the angular validations, so I included a formControlName attribute, the validation works fine and the field behaves as expected.

Until now it is ok, but when I created a directive (class with @Directive) to use as an attribute (in this case, the attribute myDirective) in the field that has the formControlName attribute and the directive has a NgModel injected, I receive the following error:

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

In the demo I use @Optional for NgModel so as to not receive the error and the page load, but I log ngModel and it shows that it is null.

If I provide NgModel explicitly in the providers it doesn't show an error, but it won't be the ngModel that I want (not related to the control/field in question), so if I try to apply changes to that ngModel it won't reflect in the field.

If, instead, I remove the formControlName, the directive works fine (you can see in the logs where I log 'ngModel:' in one line and the object in the next: ngModel was was null before and now is an object of type NgModel). The validation also happens because of the name attribute (the formBuilder binds based on the 'name' attribute of the field). The problem is that the initial value in the model doesn't show in the field, only shows after that value changes (you can see below the field where I show the model object as JSON), and also the field doesn't stay in an invalid state even if the value in it is invalid, like when I erase the content (the border doesn't change to red and inspecting in the browser the class is ng-valid, and it is also ng-untouched, even after changing, kinda like the field and the control are not connected).


Files

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

Updated

Just to clarify, what I want is the angular2 object NgModel related to the control, with methods like viewToModelUpdate and properties like valueAccessor. You can see the NgModel object logged in my plunker if you remove the formControlName attribute.

解决方案

I couldn't find the problem besides how the @Optional()arg works, so I tried to find another way to do it, see if it solves the problem for you!


Files

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], []] });
    }
}

Working plunker: http://plnkr.co/edit/nYtsXH?p=preview

这篇关于Angular 2 - 当 html 字段具有 formControlName 时,在自定义指令中注入 ngModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆