Angular2 - 从外部验证和提交表单
Posted
技术标签:
【中文标题】Angular2 - 从外部验证和提交表单【英文标题】:Angular2 - Validate and submit form from outside 【发布时间】:2016-10-29 12:10:26 【问题描述】:我有一个看起来像这样的简单表单
<form (ngSubmit)="save()" #documentEditForm="ngForm">
...
</form>
并且需要提交表格并从外部检查其有效性
例如。要么以编程方式提交,要么使用位于<form>
标签之外的<button type="submit">
。
【问题讨论】:
【参考方案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 ......) ,不管我怎么尝试。
我认为在表单标签“外部”存在问题的原因是,一旦我将提交/验证按钮放在</form>
标签内,这个问题就消失了。我陷入了这个兔子洞,但后来意识到我需要脱离表格是有正当理由的。
但后来我意识到我必须做两件事来解决这个问题,即 (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
是 另一个 指令,当您创建 <form>
标记时会自动应用。
令人困惑的注意事项:NgForm
和 FormGroupDirective
在其源代码中都有 exportAs: 'ngForm'
,(并且它们都声明了 submitted
和 ngSubmit
属性)。但是当我输入#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
)。
那么它是如何正常使用常规提交按钮的呢?
通常,如果您有 <button type='submit'>Submit</button>
(在 <form>
标签内),它将触发标准 HTML <form>
提交(与 Angular 无关) - 这会在表单标签上引发标准 submit
事件.
如果<form>
标记上也有[formGroup]
指令(如上所示),那么HTML 表单submit
事件被指令“捕获”,这就是导致上面onSubmit()
函数的原因调用。
这反过来会引发 ngSubmit
事件 - 如果您需要进行进一步处理,您可以自己捕获 - 例如显示警报。
因此,在使用材料(反应形式)控件时,调用 onSubmit
而不是 ngSubmit.emit
以使验证处理正常工作非常重要。 $event 参数可以为 null 或未定义。
进一步阅读:查看ErrorStateMatcher
(仅适用于 Angular CDK/Material)以查看确切的规则。如果解决默认值的限制变得过于复杂,您可以创建自己的。
更令人困惑: [formGroup]
指令与仅保存数据的 FormGroup
不是同一个对象。只有指令上有submitted
- 而FormGroup
有touched
、pristine
、dirty
之类的东西。
【讨论】:
@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】:了解如何做到这一点:
使用<formname>.ngSubmit.emit()
触发提交
使用<formname>.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.emit
将不将submitted
设置为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/4:nativeElement.submit() 不在 Firefox 中提交表单,但在 chrome 中有效