I was recently working on a project that loaded an iFrame
into an Angular component's template file. The source of this iFrame
is generated by a third party who uses Chase to validate payment information, and I was integrating the third party tool into our web app. One key aspect of generating this iFrame
required that we provide the url to a callback file (provided by the vendor), which their iFrame
would use to control flow, like changing the look of the buttons on the iFrame
, but calling functions in the callback file, that would need to trigger function we added on window.top
. The problem seemed relative straight forward, until I started to work on the flow out of the iFrame
and this is where NgZone comes in.
Issues
- One functionality of the
iFrame
is to validate that correct credit card (CC) information is entered, and upon failing to enter this data, we wanted to show a modal with the reasons for failure, but the modal was taking incredibly long to show up - Upon entering valid payment information, the
iFrame
logic would validate this through Chase and call a function the callback file to redirect back to the home page, but even though the redirect viarouter.navigate
was working, the styles and loading of data in the home page would be painfully slow - Even after being rerouted back to the home page successfully, other options in the page, such as tabs, were not responsive, and if they did change pages, the data in those pages would be missing
- If you just allows the page to sit, after a while the data would load and the styles would load, and the page would look normal, but it was too long
First solution
Initially, it seemed like using tick()
from ApplicationRef to "explicitly process change detection and its side-effects". This worked like a charm for the modals, and instead of them taking forever to load upon the iFrame
submission detecting an error, it was as quick as expected. I thought I had won this war. However, when submitting the iFrame
successfully and being redirected to the home page, I found that the slow loading issue continued! 🤯
Second Solution
This is when NgZone came into play. Looking back, it makes sense that this is the Angular service I needed to use, because it is a "...service for executing work inside or outside of the Angular zone". In my case, when the iFrame
made the call to Chase to validate the payment information, succeeded or failed, and returned to call a function in the vendor provider callback file, that callback file was living outside of the Angular world. The callback file lives in a publicly available location, e.g. /assets/html
so that the Chase iFrame
can access it. Therefore, when the iFrame
calls a function this callback file, that file is outside of Angular and needs to be told to run inside. Here is how this works for my use case.
i. In my angular component, I imported NgZone
in the constructor:
constructor(
private zone: NgZone,
@Inject('Window') private window: Window
){}
ii. The functions that the callback file needed to execute, not only had to go on window.top
, but needed to be wrapped the NgZone
:
private setupWindowFunctions(){ (
this.window as { [key: string]: any })['funct1'] = (res: VenRes) => {
this.zone.run(() => {
this.func1(res);
});
};
Now, when the iFrame
comes back with a successful validation or error, their callback file on our web app's public folder, triggers the window.top
function, such as func1
, and since this function lives within the NgZone
, it is executed seamlessly, avoiding the slow data load that I was seeing before.
I think is is a nugget of knowledge that only experienced Angular developers might know of the top of their hear, so hopefully it helps someone out there, cause I certainly did not know this and own it to my awesome team to have figured it out.