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

Posted

技术标签:

【中文标题】如何在 Angular 2.0 中使用 formControl 访问 Native HTML Input 元素【英文标题】:how to access Native HTML Input element using formControl in Angular 2.0 【发布时间】:2017-02-08 05:50:24 【问题描述】:

我正在使用 Angular 2.0 最终版本。

我想做什么? - 我只想在输入接收焦点时显示一些通知(警告,输入要求)。或者更好的是,当他的父母 (the div) 有 mouse hover 事件时。

从父级(div)执行此操作很容易。只需 (focus)=showNotifications() + 一个 ngIf - 就完成了。

但我想将此功能封装在 show-notifications 组件中 - 并使其可重用..

鉴于我使用@Input() 在显示通知中传递the control - 如果我可以访问native html input element,我可以同时执行这两项操作。您可以在show-notifications.component.ts 中查看如何操作。代码如下:

app.component.html:

`<div>
   <label>Name: </label>
   <input formControlName="myName">
   <show-notifications [the_control]="myName" ></show-notifications>
</div>`

app.component.ts:

export class AppComponent 
    myName = new FormControl("defaultvalue",[some validators..])

show-notifications.component.html:

<div class="show_requirements" *ngIf="hasfocus or parentDivHasMouseHover"> // incorect code and logic - i know, but you can see the idea..

    <p *ngFor="let requirement of thisInputRequirements">requirement<p>

</div>

show-notifications.component.ts:

export class ShowNotificationsComponent 

    @Input() the_control;
    this.thisInputRequirements = // take them form firebase.
    this.thisInputCustomErrorMessages = // take them from firebase.

    // I implemented all this and works amazing but i want to do:

    //something like this:

    ngOnInit()
        this.nativeInputElement = this.the_control.getNativeElement() // this of course doesn't work

        // the requirements are shown only when the input receive focus
        let source = Rx.DOM.focus(this.nativeInputElement);
        source.subscribe( x => this.hasFocus = true)

        // Or even better: 
        // the requirements are shown only when the parent has mouse hover
        // or any other event / endles posibilites here..

        let parent = getParentOf(this.nativeInputElement)
        let source = Rx.DOM.mouseover(parent);
        source.subscribe( x => this.parentDivHasMouseHover = true) // this will be some toggling functionality.
    


问题:

如果我有 formControl (myName = the_control) 对象,我如何访问本机元素?

有没有更好的方法在单独的组件中进行通知 - 并使其可重用?我已经在整个应用程序中成功地使用了它 - 显示错误和输入要求..

注意:我尝试首先使用主题标签语法 ​​(#myInput) 传递漏洞 html 输入,并在 show-notifications 组件中在那里形成,我尝试这样做:myInput.myName - 访问控件但我未定义。本机输入元素上不存在这些控件。我需要该控件进行验证:)

【问题讨论】:

一种选择是创建自定义表单控件。您可以让用户使用#input 传入 HTML,以便将其绑定到要使用的 ControlValueAccessor 字段。 【参考方案1】:

从根本上说,元素访问其兄弟元素的唯一方法是传递对它的引用。您可以为您的通知组件提供控件和表单的名称,并按照您的做法进行查找,但这是一个容易出错的过程。但是,我认为创建指令对解决您的情况更有帮助。您可以直接在输入上使用该指令。它允许您的代码像组件一样可重用,但因为它实际上是在输入上,所以它可以访问本机 html 元素。这是一个使用我编写的货币输入的示例,以了解该做什么。我为焦点事件添加了一个位置,以向您展示在哪里做事。显然,您想更改名称并使用小数管道擦除代码,但我将其留作参考。

@Directive(
  selector: '[appCurrencyInput]',
)
export class CurrencyInputDirective implements OnInit 
  @Input() runOnEveryModelChange = false;

  public reference: ElementRef<HTMLInputElement>;
  private numTimesRan = 0;
  constructor(el: ElementRef, private decimalPipe: DecimalPipe) 
    this.reference = el;
  

  @HostListener('focus', ['$event'])
  private onFocus = (focusEvent: FocusEvent) => 
    //You could do showNotifications() here, have access to focusEvent if you want
  

  @HostListener('ngModelChange', ['$event'])
  private onModelChange = (ngModelChangeEvent: number) => 
    this.numTimesRan++;
    if (this.runOnEveryModelChange || this.numTimesRan === 1) 
      const asNumber = this.decimalPipe.transform(ngModelChangeEvent, '1.2-2');
      this.reference.nativeElement.value = asNumber ? asNumber.replace(/,/g, '') : null;
    
  ;

  ngOnInit() 
    //here you could do something if you want, just like a component
  

你会这样使用它

<input formControlName="myName" appCurrencyInput>

【讨论】:

【参考方案2】:

如果你想从 Abstractcontrol 获取一些信息,因为它是一个普通的 Html 输入元素,我发现了一些可能会有所帮助的东西,例如,如果你想向它添加一个“事件侦听器”,以便知道用户何时正在与元素交互,它可以通过使用 valueChanges 或 statusChanges 来完成,这两个 Observables 都来自 AbstractControl 类,我用它来知道元素何时发生变化并在提交表单之前选择选项以执行更多操作,我离开这是我的代码示例:

getValue() 
  this.myForm.get('myControlName').valueChanges.subscribe((optionValue) => 
    console.log(optionValue);
    this.doSomething(optionValue);
  );

如果你想在用户悬停父级时制作一些动画或更改,也许你可以使用一些简单的 css 技巧,如下所示:

.parent #child
  opacity: 0;


.parent:hover #child
  opacity: 1;

您可以查看此文档以获取更多详细信息:https://angular.io/api/forms/AbstractControl#valueChanges

【讨论】:

【参考方案3】:

这是非常基本的。 FormContrl 不是必需的。 您应该使用 # 和在输入元素上创建一个局部变量 将局部变量传递给方法

<div>
<label for="firstname">First Name</label>
<input name="fname" #nameFirst />
<label for="lastname">Last Name</label>
<input name="lname" #nameLast />
</div>
<button (click)="send(nameFirst , nameLast)">Submit</button>

export class HtmlExample 

    send(firstName: HTMLInputElement, lastName: HTMLInputElement): void 
        console.log(`Hello $firstName.value  $lastName.value`);
        firstName.value = '';
        lastName.value = '';
    


【讨论】:

【参考方案4】:

我认为您可以使用# 表示法将元素传递给ShowNotificationsComponent

export class ShowNotificationsComponent 
    @Input() the_control;
    @Input() elm;
    //...

然后在模板中:

<input formControlName="myName" #elm>
<show-notifications [the_control]="myName" [elm]="elm"></show-notifications>

【讨论】:

是的@Martin,感谢您的回答,我已经这样做了,并且工作正常,但我仍在等待一个允许我访问控件内的本机元素表单的解决方案。我不敢相信他们没有以某种方式联系在一起。形成本机输入元素,我无法获得控件。从控件我无法获得本机元素。这怎么可能?听起来不太对劲。我需要在我的代码中创建所有这些 # 变量。不干净。但是,是的,现在有效:) 好吧,如果您查看FormControlAbstractControl 的源代码,它实际上不会在任何地方访问本机元素。见github.com/angular/angular/blob/2.1.0-beta.0/modules/%40angular/… 和github.com/angular/angular/blob/2.1.0-beta.0/modules/%40angular/… 所以我不完全确定,但不是nativeElementname 属性在模板驱动表单中用于命名FormGroup 中的FormControl 吗?如果是这样(使用您最喜欢的 DOM 选择器,我选择 jQuery,因为这是我脑海中最清楚的)执行以下操作...var element = $('[name= yourInputName ]'); 之后您应该能够将其作为标准输入表单元素访问。 @Alon 你有没有找到一种 Angular 的方法来做到这一点,因为我也不想在我的模板上贴满#name,或者只是为了一个自定义组件来访问 dom 元素?

以上是关于如何在 Angular 2.0 中使用 formControl 访问 Native HTML Input 元素的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2.0 和模态对话框

“Angular 2.0 for TypeScript”(alpha)动画是如何工作的?

如何在 Chrome 开发人员工具中将 CSS 样式更改保存到 ANGULAR 2.0 组件表单?

无论用户登录如何,如何保护 Angular / .NET Core 2.0 应用程序中的所有 api 调用

在 asp.net core 2.0 中使用 angular 5 上传文件。文件为空

AngularJS 2.0入门指南