我有一个Angular2应用程序,它在某个时候需要通过POST请求将用户发送到外部服务上的页面。
基本上,流程应如下
- 用户点击按钮
- 拦截并阻止默认表单提交事件
- 执行API调用是为了在后台设置一些内容并检索一些值以传递给外部服务
- 一些隐藏的表单字段填充有从API检索到的数据
- 表单提交随后被解雇
据我所知,在Angular中没有办法执行"POST到外部页面"类型的操作,必须用ngNoForm
覆盖Angular的表单处理,并允许浏览器将其作为常规POST处理。
所以,目前,我有一个这样的模板:
<form ngNoForm (submit)="proceedToService($event)" method="POST" action="https://some-service.com/etc">
<input type="hidden" name="token" [value]="someToken">
<button type="submit">Proceed to external service</button>
</form>
在组件中,我有这样的:
public someToken:string="";
proceedToService($event) {
$event.preventDefault();
this.apiHandler.doSomeStuff().subscribe(
(someStuff) => {
this.someToken = someStuff.token;
$event.target.submit();
}
);
}
(doSomeStuff
返回Observable)
这种方法的问题是,提交事件似乎在DOM中填充值之前就被触发了,这意味着POST请求中的token
字段是空的。
我能够通过将$event.target.submit();
封装在setTimeout调用中使其工作,如下所示:
setTimeout(() => {$event.target.submit()}, 1000);
但这感觉像是一种超级黑客的方法,无论如何都可能失败(如果更新需要1秒以上怎么办?)而且很难测试。
那么,有没有更好的方法来实现这一点呢?理想情况下,我想做的是观察隐藏输入的值在DOM中的实际更新,然后在那时触发提交。但到目前为止,我还无法找到实现这一点的方法——例如,ngModel绑定不支持隐藏字段。
我对Angular世界非常陌生,尤其是Angular2,所以我可能从完全错误的角度来处理这个问题,但任何帮助都将不胜感激。
您可以通过使用模板引用变量和button type="button"组合来实现这一点。
<form ngNoForm #extForm method="POST" action="https://some-service.com/etc">
<button type="button" (click)="proceedToService(extForm)">External Service</button>
</form>
打字稿功能
proceedToService(frm) {
this.apiHandler.doSomeStuff().subscribe(
(someStuff) => {
var input = document.createElement("input");
input.type = "hidden";
input.name = "token";
input.value = someStuff.token;;
frm.appendChild(input);
frm.submit();
}
);
}
我希望这能帮助你
我使用AfterViewChecked
找到了一个潜在的解决方案,如下所示:
private pendingEvent;
proceedToService($event) {
$event.preventDefault();
this.apiHandler.doSomeStuff().subscribe(
(someStuff) => {
this.someToken = someStuff.token;
this.pendingEvent = $event;
}
);
}
ngAfterViewChecked() {
if ( this.pendingEvent ) {
this.pendingEvent.target.submit();
this.pendingEvent = null;
}
}
这似乎符合我的要求——因为ngAfterViewChecked
是在检查视图后调用的,视图是在更新后检查的(我认为是?),所以此时值填充正确。
同样,在我对Angular不熟悉的情况下,我不确定这是否是最佳解决方案,甚至是实践中可行的解决方案,但它至少比使用setTimeout"感觉"更好。。。