Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Improved iframe integration #454

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

punkch
Copy link
Contributor

@punkch punkch commented Aug 17, 2022

I had a use case where a specific form field in the mobile app was fed by the field users by scanning a QR code.
However, for the web form counterpart, there is no possibility in enketo for barcode scanning, so I've needed a way around this limitation. Luckily the system that integrates with enketo and central, is showing the webforms in an iframe and it already "knew" the possible values for this QR code field, so I've opted to improve on the iframe functionality in order to be able to set this QR code via an iframe message from the parent window.

What this PR does, is to first add a new event called 'forminitialized'. This new event is fired when the webform is successfully loaded. In addition, enketo will post messages to the parent window for all the events, rather than just SubmissionSucces, Edited and Close. This eventually allows for tighter integration between both applications. The third, and probably the biggest change this PR adds is a listener to the window message event. Messages from the parent window should be objects with just type and content property. Type is something like a command, while content is an object with shape specific to the message type. At the moment, the handler will only react to messages of type 'setfields'. Their content should be key-value pairs of the field paths and the URI encoded values to set. I've tried my best to document this in the new method's jsdoc.
At the end, what my parent application now does is to wait for the 'forminitialized' event and then post the 'setfields' message for that QR code field, where the value is selected via lookup in the 'parent' system.

Closes #

I have verified this PR works with

  • Online form submission
  • Offline form submission
  • Saving offline drafts
  • Loading offline drafts
  • Editing submissions
  • Form preview
  • None of the above
    I could only verify this with form previews and online form submission as these are the only parts I am using enketo for at the moment.

What else has been done to verify that this works as intended?

Why is this the best possible solution? Were any other approaches considered?

I was trying to set default values in the form via url parameters, but it wouldn't allow for further interactions after the form was loaded. Also the pull request I've sent in this regard created some unexpected regressions.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

I've done my best to isolate these changes for when only enketo is loaded in an iframe and there is parentWindowOrigin configured in the settings. In addition messages posted to the enketo window and are not from the expected origin will be ignored. Also, I've only tested this with "text" input fields, but I think that the method I am using form.input.setVal(inputDomElement, someValue) should work for any widgets.

Do we need any specific form for testing your changes? If so, please attach one.

No, this should work with any form. Here is some sample code on how to set the a form field named "Location_Id" with value "Some Location->123456-789->{8e071d7c-5829-477c-b1fb-c67cbfd64894}"

var locationName = encodeURIComponent('Some Location-\x3e123456-789-\x3e{8e071d7c-5829-477c-b1fb-c67cbfd64894}');
var message = {type:'setfields', content:{'/data/Location_Id':locationName}};
webform.postMessage(message);

Thank you very much for your time and consideration and let me know if I can somehow make this better.

@punkch punkch force-pushed the feat/more-iframe-messages branch 2 times, most recently from 05d6c9b to 361e6ed Compare August 17, 2022 09:25
- post all events as messages to parent window when in iframe.
- handle setfields message posted from the parent window
Copy link
Contributor

@eyelidlessness eyelidlessness left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you can achieve what you want without any changes to enketo-express, by accessing the iframe's DOM directly. E.g.

const iframe = document.querySelector('iframe');

iframe.addEventListener('load', () => {
    const iframeGlobal = iframe.contentWindow;
    const iframeDocument = iframeGlobal.document;

    // ...
});

This won't give you access to form, but it gives you direct DOM access and allows you to set values and dispatch events as you wish. Would this be sufficient for your use case?

@punkch
Copy link
Contributor Author

punkch commented Oct 21, 2022

Well, I could probably do just with DOM access, but the iframe integration was already there (with very limited functionality) so I thought to improve it.

  • The new FormInitialized event would signal that enketo is fully done processing the webform which would be a bit later than the DOM load. So I could end up changing a field and enketo to blank it out a bit later.
  • Posting messages for all events (rather than specific few) to the parent window, opens room for tighter integration. i.e do something in the parent app on DataUpdate
  • Using the form felt as the most future proof way to change a field value in enketo.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants