有没有办法在没有 redirect/reload 的情况下为 Pardot 提交 () HTMLFormElement 表单?如果不是,什么是 Angular 模拟方法?

Is there a way to submit() an HTMLFormElement form for Pardot without a redirect/reload? If not, what is an Angular way to emulate same?

我会尽量简化这件事。我已经尝试了很多东西并且已经对此进行了一段时间的研究,我想我遗漏了一些小东西。

使用 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 请求,例如在“网络”选项卡中。

我对此的问题是数据被发送到third-party供应商,然后发生重定向,重定向到我们告诉他们的任何地方(在这种情况下,这是同一页)。所以用户体验是,不仅这个表单数据而且页面上的所有其他内容都被清除了。

我所看到的关于我要问的内容的大多数示例本质上是不使用 action/method/submit 方法,而是定义一个从头开始创建 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 });
}

所以我有点卡住了,想知道是否有许多伟大的头脑以前解决过这个问题,但我就是找不到。

我还注意到有很多额外的 headers 随表单的 submit() 请求一起发送,但我不知道它们来自哪里 - 想知道这是否有助于解决 CORS 问题。 Headers 喜欢带有“visitor_id”类型条目和其他条目的“Cookie”。


我想到的可能的解决方案:

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

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

一如既往,感谢大家的帮助。


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

拼凑来自 and 的信息,我能够导入 HttpJsonpModule 并将其与

一起使用
http.jsonp(url + urlEncodedString, 'callback');

然后,我将 success/error URL 配置为 statically-hosted json 文件,这些文件将在各自的响应中返回,用于 success/error 回调(到我的理解)。我将在接下来的几天内对其进行测试,但只想提供当前更新。

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

更新: 我不是使用 HTMLFormElement.submit(),也不是 http.post,而是使用 http.jsonp() 请求完成我的解决方案,只是因为支持 JSONP server-side.

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

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

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

angular documention is very well written 其实挺有意思的。 angular 实现表单的方式非常完整,可以让您充分控制所发生的事情。

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

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

我的第一个问题的简单答案是无法完成。无论您做什么,您的页面位置都必须更改。如有不妥请指正。

我的第二个问题的中等答案是,如果您可以模仿表单的请求并使用Angular 的 HttpClient 的 http.post 或改为获取请求。但这对我不起作用,因为我无法模仿所有 headers or/and 在表单处理程序端(第 3 方服务器,Pardot)存在我无法控制的 CORS 问题。

一般情况下的解决方法是在 <iframe> 中托管表单,如果您可以说服您的团队就可以了:)。然后,redirect/reload 行为只会发生在 <iframe> 内,而不是整个页面。


我的解决方案是通过我们的第 3 方供应商寻找另一种支持方法,因为我被告知要避免 <iframe>s。 Angular 和 Pardot 支持的 JSONP 协议有点(我觉得)可疑,它解决了 CORS 问题。但是这个设置对于这个任务有点矫枉过正。完整详情如下:

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

    导入 HttpClientJsonModule
  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. 我托管了两个可用作 success/error 重定向的简单 .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>

你可以在那里做任何你想做的事——我们的团队只是想要一个请求去我们的后端服务器记录成功或错误。 (我们用 environment-specific 值替换 #{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.js,并为错误重定向设置类似内容(当前标签是“错误位置”)到我们的 error.js。例如,

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


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