Angular2 - 从外部验证和提交表单

Posted

技术标签:

【中文标题】Angular2 - 从外部验证和提交表单【英文标题】:Angular2 - Validate and submit form from outside 【发布时间】:2016-10-29 12:10:26 【问题描述】:

我有一个看起来像这样的简单表单

<form (ngSubmit)="save()" #documentEditForm="ngForm">
...
</form>

并且需要提交表格并从外部检查其有效性

例如。要么以编程方式提交,要么使用位于&lt;form&gt; 标签之外的&lt;button type="submit"&gt;

【问题讨论】:

【参考方案1】:

您可以使用按钮上的表单属性将按钮链接到表单:

<form (ngSubmit)="save()" id="ngForm" #documentEditForm="ngForm"> 
  ... 
</form>

<button form="ngForm">
  SAVE
</button>

你仍然可以像这样检查它的有效性:

<button form="ngForm" [disabled]="!documentEditForm.form.valid">
  SAVE
</button>

表单需要有一个 ID id="example-form" 并且提交按钮在form="example-form" 中有一个匹配的 ID

更多详情请看这里:https://developer.mozilla.org/en-US/docs/Web/html/Element/button#attr-form

【讨论】:

我只想再添加一件事。似乎 Angular 并不关心按钮在表单之外被禁用 - 即,如果您在输入元素上按 enter,它仍然会尝试提交它。我不确定这是否真的是 Angular 本身的一个错误,或者还有其他解决方案。一个快速的解决方法就是去 (ngSubmit)="form.valid && save()" 仅供参考 IE 和 Edge 不支持 'button' 上的 'form' 属性,所以这在那里不起作用(afaik)。 谁说用户体验需要一个 GUI 按钮?考虑一个“文本验证”表单。其中许多人会在文本长度达到6 数字时立即提交表单。如果您想隐藏按钮并在该按钮上触发click,您仍然在同一条船上。 您需要某种方式来直接调用.submit().ngSubmit.emit() 方法但我喜欢你提供了一个可靠、直接的解决方案,所以,谢谢 :) documentEditForm 尚未定义! @bob Edge 从 Edge 16 开始支持此功能(see MDN on compatibility 详细信息)【参考方案2】:

我正在写这个答案,希望它对某人有所帮助。我花了一整天的时间试图解决这个问题,遵循其中的每个答案和 cmets ......但我仍然收到错误消息 #documentEditForm (我的等价物)未找到(无法读取未定义的属性 x ......) ,不管我怎么尝试。

我认为在表单标签“外部”存在问题的原因是,一旦我将提交/验证按钮放在&lt;/form&gt; 标签内,这个问题就消失了。我陷入了这个兔子洞,但后来意识到我需要脱离表格是有正当理由的。

但后来我意识到我必须做两件事来解决这个问题,即 (1) 在验证时将 ? 添加到 ngForm 的名称和 (2) 实现 ViewChild(NgForm) documentEditForm!: NgForm

<form #documentEditForm="ngForm">
...
</form>

<button class="btn-save button primary" (click)="save()" [disabled]="documentEditForm?.invalid">
   SAVE
</button>

component.ts 内部:

import  Component, OnInit, ViewChild  from '@angular/core';
import  NgForm  from '@angular/forms';

export class XYZComponent implements OnInit 

  @ViewChild(NgForm) documentEditForm!: NgForm;

...

在 Angular 中使用模板驱动的表单时,如果您使用 #form_name="ngForm" 语法,您会将其暴露在表单内部。但是,当您使用 ViewChild 时,您会将表单公开给整个组件,而不仅仅是在表单内部。请记住,如果应用程序编译失败或出现意外输出/错误,请尝试使用旧的 ?

【讨论】:

【参考方案3】:

这对我有用。

<form #editForm="ngForm">
  <button type="button" (click)="editForm.submitted = true; editForm.ngSubmit.emit(); anotherMethod();">
    Submit programatically
  </button>
</form>

关键是同时设置submitted = true 和发射ngSubmit 事件。

【讨论】:

“不能分配给'提交',因为它是一个只读属性” - 这是我得到的错误【参考方案4】:

重要提示:如果使用 Angular 材质表单控件 + 反应表单

调用onSubmit(undefined)[formGroup] 指令上正确设置submitted = true

注意:directive 与 angular form 本身不是同一个对象(更多内容见下文)

这是sourcecode for the [formGroup] directive 的一部分。 (对于反应形式)

@Directive(
  selector: '[formGroup]',
  providers: [formDirectiveProvider],
  host: '(submit)': 'onSubmit($event)', '(reset)': 'onReset()',
  exportAs: 'ngForm'
)
export class FormGroupDirective extends ControlContainer implements Form,
    OnChanges 
  /**
   * @description
   * Reports whether the form submission has been triggered.
   */
  public readonly submitted: boolean = false;

 .....

 onSubmit($event: Event): boolean 
    (this assubmitted: boolean).submitted = true;
    syncPendingControls(this.form, this.directives);
    this.ngSubmit.emit($event);
    return false;
 

你可以这样使用它:

<form [formGroup]="form" #formRef="ngForm">

您可以在ts 文件中获得对FormGroupDirective 的引用:

@ViewChild('formRef') 
formRef: FormGroupDirective;
注意:NgForm另一个 指令,当您创建 &lt;form&gt; 标记时会自动应用。 令人困惑的注意事项:NgFormFormGroupDirective 在其源代码中都有 exportAs: 'ngForm',(并且它们都声明了 submittedngSubmit 属性)。但是当我输入#ngForm='ngForm' 时,我得到一个FormGroupDirective 类型的对象,而不是NgForm(在调试器中验证)。我不确定具体原因 - 但这就是为什么我将其声明为 FormGroupDirective 而不是 NgForm - 我想可能是第一个获胜。

你可以这样使用它:

this.formRef.onSubmit(undefined)

例子:

// html
<form [formGroup]="form" #formRef="ngForm">
    // ...Form Controls
</form>

// component.ts
export class MyComponent 

    @ViewChild('formRef')
    formRef: FormGroupDirective;

    form: FormGroup = new FormGroup(
        myInput: new FormControl(''),
        //etc...
    );

    submitFormProgrammatically() 
        this.formRef.onSubmit(undefined);
    

如果您只是打电话给this.formRef.ngSubmit.emit(),因为其他一些答案表明您不会得到所有重要的submitted = true 集。

为什么这很重要?

如果您使用任何 Angular CDK 或 Angular Material 控件,则不会显示错误情况,除非已触摸(单击或获得焦点)表单字段或已提交整个表单。

因此,如果您缺少鼠标/光标从未输入过的必填字段,那么即使您输入了ngSubmit.emit(),该字段也不会显示为红色(因为submitted = false 用于窗体和控件有touched = false)。

那么它是如何正常使用常规提交按钮的呢?

通常,如果您有 &lt;button type='submit'&gt;Submit&lt;/button&gt;(在 &lt;form&gt; 标签内),它将触发标准 HTML &lt;form&gt; 提交(与 Angular 无关) - 这会在表单标签上引发标准 submit 事件.

如果&lt;form&gt; 标记上也有[formGroup] 指令(如上所示),那么HTML 表单submit 事件被指令“捕获”,这就是导致上面onSubmit() 函数的原因调用。

这反过来会引发 ngSubmit 事件 - 如果您需要进行进一步处理,您可以自己捕获 - 例如显示警报。

因此,在使用材料(反应形式)控件时,调用 onSubmit 而不是 ngSubmit.emit 以使验证处理正常工作非常重要。 $event 参数可以为 null 或未定义。

进一步阅读:查看ErrorStateMatcher(仅适用于 Angular CDK/Material)以查看确切的规则。如果解决默认值的限制变得过于复杂,您可以创建自己的。

更令人困惑: [formGroup] 指令与仅保存数据的 FormGroup 不是同一个对象。只有指令上有submitted - 而FormGrouptouchedpristinedirty 之类的东西。

【讨论】:

@ViewChild('ngForm') ngForm: NgForm; // ngForm 是 FormGroupDirective 的一个实例,而不是 NgForm【参考方案5】:

此示例适用于 Angular 6 及更高版本

<form (ngSubmit)="save()" id="ngForm" [formGroup]="form"> 
    ... 
</form>

<button type="submit" class="btn-save button primary" form="ngForm">Save</button>

【讨论】:

这是一个不错的解决方案,但它在 IE 中不起作用 :( 你可以在这里查看:w3schools.com/tags/att_button_form.asp【参考方案6】:

了解如何做到这一点:

使用&lt;formname&gt;.ngSubmit.emit() 触发提交 使用&lt;formname&gt;.form.valid 获取表单状态

例子:

<form (ngSubmit)="save()" #documentEditForm="ngForm">
...
</form>

<button class="btn-save button primary"
(click)="documentEditForm.ngSubmit.emit()"
[disabled]="!documentEditForm.form.valid">SAVE</button>

编辑:正如@yuriy-yakovenko 所指出的,您应该在组件代码中添加以下内容:

@ViewChild('documentEditForm') documentEditForm: FormGroupDirective; 

如果您还没有完成,请不要忘记导入FormGroupDirective

【讨论】:

ngSubmit 未定义 @VievChild('documentEditForm') documentEditForm: FormGroupDirective;也需要 如果我调用 emit 那么我的 ngForm 提交(布尔属性)也不会更改为 true。你能帮忙吗? 是的,我和@ParthaSarathiGhosh 有同样的问题 重要提示:调用.ngSubmit.emitsubmitted 设置为true。如果您关心处理表单是否已提交,请改用.onSubmit(null),否则.submitted 将为false。【参考方案7】:

以下解决方案适用于我的情况,请尝试这个简单的解决方案。我不确定它是否适用于所有条件:

<form #documentEditForm="ngForm" id="ngForm" (ngSubmit)="save(documentEditForm.valid)"> 
    ...Your Input Elements... 
</form>

按钮应该像这样在表单之外声明:

<button form="ngForm">Submit</button>

表单的验证应通过以下条件检查到 save()

save(isValid:boolean)
    if(isValid) 
        ...Your code to save details...
    

希望这个简单的解决方案对您有所帮助。

【讨论】:

【参考方案8】:

如果您使用的是响应式表单,请使用 formGroup 无效属性来禁用提交按钮:

<button  
  form="ngForm"
  [disabled]=" editor.invalid>Enviar</button> 

...

<form [formGroup]="editor"  id="ngForm"   (ngSubmit)="save()" novalidate >
...
</form>

【讨论】:

【参考方案9】:

一个对我有用的技巧

反应式表单 Angular2 包括。浏览器

是这样吗:

<!-- real button will simulate click on invisible button (cf. form) -->
<button onclick="document.getElementById('hiddenSaveButtonForMicrosoftWithLove').click()">
  The Real Button outside forms
</button>

<form>
  <!-- will be called in the background and is never visible -->
  <button id="hiddenSaveButtonForMicrosoftWithLove" type="submit" style="display: none;">hiddenSaveButtonForMicrosoftWithLove</button>
</form>

【讨论】:

我会建议@Yodacheese 提供的正确答案,因为它是基于问题的最准确答案,但我投了这个答案,因为它经过了实战考验。这么多年以来我一直使用这种方法,并且在某些情况下我仍然使用它。我建议添加一个 [disabled] 属性到两个按钮,如果你想通过点击输入来阻止表单提交,如果无效。 感谢@Sparker73 - 如果您提供完整的解决方案,我们很乐意编辑

以上是关于Angular2 - 从外部验证和提交表单的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ANGULAR 2 中提交表单时重置表单验证

使用 parsley.js 验证表单提交外部表单标记

如何提交表单服务器Angular2

Angular 2/4:nativeElement.submit() 不在 Firefox 中提交表单,但在 chrome 中有效

如何在提交按钮表单中使用路由 - Angular2

使用链接发送到表单的变量从表单外部的链接提交表单?