2

我会尽量让这件事变得简单。我已经尝试了很多东西并且已经研究了一段时间,我想我错过了一些小东西。

使用 Angular 12。

样品形式:

    <form id="my-form" action="https://example.com" action="post">
        <input name="fname">
        <button (click)="mySubmitFunction()">SUBMIT</button>
    </form>

我有mySubmitFunction()这样的逻辑:

  const myForm = document.querySelector('#my-form') as HTMLFormElement;
  myForm.submit();

使用这种方法,会发生表单提交,它需要每个<input>具有name属性来填充表单数据。您可以在 Chrome 的开发者工具中看到生成的 http 请求,例如在网络选项卡中。

我的问题是数据发送给第三方供应商,并且发生重定向,重定向到我们告诉他们的任何内容(在这种情况下,它是同一页面)。因此,用户体验是不仅此表单数据,而且页面上的所有其他内容都被清除。

我所看到的大多数示例基本上都是不使用操作/方法/提交方法,而是定义一个按钮单击方法,从头开始创建一个 http 请求(这是我实际尝试的第一件事)。问题在于,http.post无论在什么环境下,我都会在请求中遇到 CORS 问题。这基本上是我在那里做的事情:

 postContactInfoToVendor(requestBody: string): Observable<any> {
   const headers = {
     'Content-Type': 'application/x-www-form-urlencoded'
    };
  // Where the requestBody is formatted like 'fname=Bob'
  return this.http.post(environment.vendorUrl, requestBody, { headers });
}

所以我有点卡住了,想知道那里的许多伟大的思想是否已经解决了这个问题,我就是找不到。

我还注意到随着表单的 submit() 请求发送了很多额外的标头,但我不知道它们来自哪里 - 想知道这是否有助于解决 CORS 问题。诸如带有“visitor_id”类型条目的“Cookie”之类的标头等。


我正在考虑的可能解决方案:

  1. iframe在页面上托管我的联系表格。然后在我们能够在供应商方面配置的成功重定向上,可以转到感谢页面。

  2. 以某种方式拦截 form.submit() 的请求,取消该请求并发送一个使用完全相同的标头建模的新请求。不过,我目前无法拦截这些请求。

一如既往,感谢您的帮助。


更新:我的具体问题是 Pardot 表单提交。Angular 中有一个解决方法,尽管我不确定我对此有何感受。

将来自Simulate a JSONP response with JavaScript URLsHow to make a simple JSONP asynchronous request in Angular 2 中的信息拼凑起来?,我能够导入HttpJsonpModule并使用它

http.jsonp(url + urlEncodedString, 'callback');

然后,我将成功/错误 URL 配置为静态托管的 json 文件,这些文件将在相应的响应中返回,以用于成功/错误回调(据我所知)。我将在接下来的几天内对其进行测试,但只是想提供最新的更新。

虽然,一般来说,我知道我的<iframe>解决方案也会奏效:)

更新:我已经完成了我的解决方案,不是使用 HTMLFormElement.submit(),也不是 http.post,而是一个 http.jsonp() 请求,只是因为服务器端支持 JSONP。

我很快就会在这里写下我自己的答案以及所有信息。

4

2 回答 2

0

Angular 有两种主要的方式来实现表单:

  • 模板驱动的表单
  • 反应形式

角度文档写得非常好,实际上非常有趣。Angular 实现表单的方式非常完整,可以让你对发生的事情有很大的控制权。

要回答您的问题,您的第二个解决方案非常接近 angular 所做的。Angular(ngSubmit)在表单上使用输出指令,您可以在此处找到它,并在许多示例中使用。(ngSubmit)每当单击具有 a 的按钮时触发type="submit"(您也可以通过编程方式触发)。

您可以在此处找到模板驱动的表单实现的简单示例:https ://angular.io/start/start-forms

于 2021-07-12T17:56:02.833 回答
0

我的第一个问题的简单答案是它无法完成。无论您做什么,您的页面位置都必须更改。如果这是错误的,请有人纠正我。

我的第二个问题的中等答案是,如果您可以模仿表单的请求并使用 Angular 的 HttpClient 的 http.post 或 get 请求,它可能可以通过 http 请求与 HTMLFormElement 的 .submit() 来完成。但这对我不起作用,因为我无法模仿所有标题或/并且表单处理程序端(第 3 方服务器,Pardot)存在我无法控制的 CORS 问题。

一般情况下的解决方法是将表单托管在<iframe>. 然后,重定向/重新加载行为只会发生在 中<iframe>,而不是整个页面。


由于有人告诉我避免使用<iframe>s,我的解决方案是通过我们的第 3 方供应商找到另一种支持方法。Angular 和 Pardot 支持一个有点(我觉得)不太可靠的协议,称为 JSONP,它可以解决 CORS 问题。但是这个设置对于这个任务来说有点矫枉过正。完整详情如下:

  1. 在我的模块 app.module.ts 中,我必须HttpClientJsonModule@angular/common/http.

  2. 我为我们创建了一个包含所有 Pardot 预期字段的界面。

  3. 在按下提交按钮时激活的功能中,我从表单字段构建输入到该界面的表单,并从如下服务提交:

    postContactInfoToPardot(formData: PardotForm): Observable<any> {
      const formParams = parameterize(formData);

      /*
      Angular adds &callback=ng_jsonp_callback_0 or ng_jsonp_callback_1 etc. to the request, which is the name of
        the success callback (will not get hit in this case bc we are not calling it directly)
      'callback' is the name of the parameter that Pardot expects
      There doesn't seem to be a way for our defined redirected .js to know about the number in ng_jsonp_callback_0,
        so just eat the error that will say "JSONP injected script did not invoke callback" in caller's error
        callback.
       */
      return this.http.jsonp(`${environment.pardotUrl}?${formParams}`, 'callback');
    }

...

// A function we have defined as a utility function
function parameterize(body: any): string {
  return new HttpParams({fromObject: body}).toString();
}
  1. 我托管了两个简单的 .js 文件,它们可以用作成功/错误重定向。在src/assets/pardot我有pardot-response-error.jspardot-response-success.js

  2. 这些文件的内容是

// Defined in src/index.html
pardotCallback({ 'result' : 'error' })

// Defined in src/index.html
pardotCallback({ 'result' : 'success' })

分别。

  1. src/index.html中,我有:
    <script>
        function pardotCallback(response) {
            const xhr = new XMLHttpRequest();

            if (response.result === 'success') {
                xhr.open('POST', '#{loggingUrl}#?level=info');
                xhr.send('Pardot form submission success');
            } else {
                xhr.open('POST', '#{loggingUrl}#?level=error');
                xhr.send('Pardot form submission error');
            }
        }
    </script>

你可以在那里做任何你想做的事情——我们的团队只是想要一个请求去我们的后端服务器来记录成功或错误。(我们用特定于环境的值替换#{loginUrl}#。我不能为此使用我的环境文件,所以这是通过我们的管道完成的)

  1. 我这边的最后一件事是处理错误。在我调用我的服务函数将表单数据发送到 Pardot 的地方,如果我们不处理这个错误,最终用户将在他们的控制台中将其视为每次表单提交的错误:
      // Due to the nature of Angular + http.jsonp + Pardot, we will have an error, every time.
      this.service.postContactInfoToPardot(pardotForm).subscribe(
        () => {},
        (err: HttpErrorResponse) => {
          // ONLY deal with an error here if it is not the one we are expecting every time
          if (err.error.toString().indexOf('JSONP injected script did not invoke callback') === -1) {
            // TODO: Replace with a call to the logging service, but this block also should never run
            console.error(err);
          }
        }
      );
  1. 现在,最后,在 Pardot 表单处理程序配置方面,我们将成功重定向(当前标签为“Success Location”)设置为我们的产品success.js,并为错误重定向设置类似的内容(当前标签为“错误位置") 到我们的 error.js。例如,

Success Location = https://<host>/assets/pardot/pardot-response-success.js


如您所见,这种设置非常奇怪,我认为我不会向未来的团队推荐它,但它确实有效。我希望这对任何寻找相同信息的人有所帮助,无论是特定于 Pardot 还是关于我的原始问题。

于 2021-07-20T17:56:00.640 回答