是否可以获得formControl的本机元素?

Posted

技术标签:

【中文标题】是否可以获得formControl的本机元素?【英文标题】:Is it possible to get native element for formControl? 【发布时间】:2017-01-31 05:43:18 【问题描述】:

我有 Angular reactive form。我创建了formControls 并将其分配给[formControl]=... 的输入字段。据我了解,它会创建 nativeElement <-> formControl 链接。

我的问题:formControl 可以得到nativeElement 吗?我想做类似myFormControl.nativeElement.focus()

【问题讨论】:

你能显示一些代码吗?告诉我们您想要的位置和方式? 更新了问题描述 但是当你想集中注意力的时候呢?那是小鬼。 你有一个或多个表单控件吗? 【参考方案1】:

我可以分享一个糟糕的解决方案,但它对我有用。

在反应形式中,我们可以使用任何一种

1) FormControlDirective

ts

myControl = new FormControl('')

模板

<input type="text" [formControl]="myControl">

2) FormControlName

ts

myForm: FormGroup;

constructor(private fb: FormBuilder) 

ngOnInit() 
  this.myForm = this.fb.group(
    foo: ''
  );

模板

<form [formGroup]="myForm">
  <input type="text" formControlName="foo">
</form>

所以对于这些指令我可以写一些补丁,比如

1) FormControlDirective

const originFormControlNgOnChanges = FormControlDirective.prototype.ngOnChanges;
FormControlDirective.prototype.ngOnChanges = function() 
  this.form.nativeElement = this.valueAccessor._elementRef.nativeElement;
  return originFormControlNgOnChanges.apply(this, arguments);
;

2) FormControlName

const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function() 
  const result =  originFormControlNameNgOnChanges.apply(this, arguments);
  this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
  return result;
;

之后我们可以轻松访问具有FormControl 实例的原生元素

1) FormControlDirective

focusToFormControl() 
  (<any>this.myControl).nativeElement.focus();

2) FormControlName

focusToFormControlName(name) 
  (<any>this.myForm.get(name)).nativeElement.focus();

Plunker Example

【讨论】:

嗨,我在组件类的顶部添加了原型,但我收到一个错误,即 this.valueAccessor._elementRefFormControlName 中未定义。【参考方案2】:

下面的代码不适用于纯 ngModel 绑定,所以我做了很多实验。最新的,Maximilian Schwarzmuller 也证实应该是这个:

@Directive(
    selector: '[ngModel], [formControl]', // or 'input, select, textarea' - but then your controls won't be handled and also checking for undefined would be necessary
)
export class NativeElementInjectorDirective 
    constructor(private el: ElementRef, private control : NgControl, @Optional() private model : NgModel) 
        if (!! model)
            (<any>model.control).nativeElement = el.nativeElement;
        else
            (<any>control).nativeElement = el.nativeElement;
    

因此,如果在主模块中提供并导出此指令,它将为所有 FormControl 附加一个自定义的 nativeElement 属性

很遗憾它不是开箱即用的......

【讨论】:

用法不清楚,如果您只是简单地将该指令导入到主模块,它会自动提供nativeElement属性吗?我的表单控件无法识别该属性,并且没有关于属性完成/预测的建议。最好发布如何使用您的指令 @Shift'NTab 不管它是我的还是其他人的或角度指令,它们的使用方式相同。如果您在自定义模块中声明该指令,则需要将其导入到您要使用它的模块中。或者只是在你的模块中声明它并将类添加到模块的声明数组中。一旦那里它会自动执行。如果您不相信,您可以将调试器放入 ctor 中。 @Shift'NTab 所以,简而言之:在你实现(声明)指令的地方创建一个模块,并确保你也导出它。然后只需将模块包含在您需要的地方。就是这样。 ok 再次回到这里 (&lt;any&gt;control.control).nativeElement 有一个错误,因为控制台中的 control.control 返回未定义。这会阻止您分配动态属性。 @LeonardoX 更新了***.com/a/49462046/4524989,所以它现在应该可以工作了,问题是原始样本是用于模板驱动的方法,其中 NgModel 被注入到控件中,而通过模型驱动,你会得到直接的 NgControl给定的输入。只需查看更新。顺便提一句。感谢 StackBlitz,很抱歉花了 5 天时间(有很多工作):) 使用该调试,它对我有很大帮助...【参考方案3】:

为 baHI 答案添加了小修复(将逻辑移至 OnInit)。 cmets中提到的错误可能与表格的变化有关。此答案适用于 "@angular/forms": "~7.1.0",

    @Directive(
      selector: '[ngModel]'
    )
    export class NativeElementInjectorDirective implements OnInit 
        constructor (private el: ElementRef, private control : NgControl) 

        ngOnInit()
          (this.control.control as any).nativeElement = this.el.nativeElement;
        
    

【讨论】:

这适用于我们在角度 6 上:D 唯一需要的更改是“选择器:[formControlName]”谢谢!!【参考方案4】:

是的,您必须使用[formControl], [formControlName] 选择器编写指令。 完整示例:

import  Directive, ElementRef  from "@angular/core";
import  NgControl  from '@angular/forms';

@Directive(
   selector: '[formControl], [formControlName]'
)
export class ControlErrorsDirective 
    get control() 
       return this.controlDir.control;
    

    constructor(
        private controlDir: NgControl,
        private host: ElementRef<htmlFormElement>) 
    
    ngOnInit() 
        console.log(this.host.nativeElement);
    

在你的 html 中使用 formControlName 像这样:&lt;input formControlName='name' /&gt;

【讨论】:

我使用此代码为每个表单控件添加动态错误消息。 不链接表单控件相关的原生元素如何获取? 这个想法(见我的回答 - 15 个赞成票)很简单:使用一个指令来简单地扩展角度控制对象和一个额外的 nativeElement 属性。从那时起,如果您引用该控件,您也可以访问该属性。但您可能只需要一个模板参考!? 为什么需要两个选择器?我发现只有一个选择器:“[formControlName]”有效。一个实际的选择器 formControl 不起作用。【参考方案5】:

你好,谁读到这里!我希望你和我玩 Angular 一样开心:)

我花了一点时间处理一个相关问题,导致我来到这里。 在我看来,“injector”指令解决方案是一种解决方法,也是一种解决问题的不可预测的方式。

我怀疑 Angular 团队可能有他们的理由说明这不是“开箱即用”的。也许他们不想将 FormControl 绑定到一个确切的 DOM 元素,因为这两个元素可以相互寿命更长或被删除(在本机元素的情况下),或被克隆(不会复制“扩展”分配,并且,因此,有效地丢失了针对原始 DOM 元素和 FormControl 对象的先前克隆的指令的任何分配)等。

经过一番思考,我选择了这个解决方案:

// Get the first invalid formControlName to scroll to: 
const firstInvalidField = 
  Object.keys(form.controls).find(field => form.get(field)?.invalid);

// My DOM element query selector string
const selector = `[formControlName=$firstInvalidField]`;

console.log(
  document.querySelector( $selector )`
);

现在我保证我将获得对 DOM 元素的引用。

【讨论】:

此解决方案不支持嵌套 formGroups 或表单控件数组。感谢您的努力 @user2599470 这是非常合理的方向......而且完全有道理。虽然 document.querySelector 在 Angular 的上下文中感觉很hackish。【参考方案6】:

在模板上创建一个输入字段:

<input matInput formControlName="cardnumber" #cardnumber >

在类中获取它的viewchild:

@ViewChild("cardnumber") cardnumberField: FormControl;

初始化:

this.cardnumberField = new FormControl(null, []);

随心所欲:

(<any> this.cardnumberField).nativeElement.value = "";
(<any> this.cardnumberField).nativeElement.focus();

【讨论】:

以上是关于是否可以获得formControl的本机元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 2.0 中使用 formControl 访问 Native HTML Input 元素

Angular FormControl valueChanges 不起作用

获取 FormGroup/FormControl 中存在的验证器

在自定义 Angular 组件中访问 FormControl

如果从父级重新初始化 FormGroup,自定义组件 FormControl 会中断

如何使用路由器磁通本机进行延迟加载以获得更好的性能?