Skip to main content

Centering a Pop-Up Form of a Different Scope

Slate offers a form embed widget in portals, but what if you want to open a Slate form in a different scope? ๐Ÿค”

In our example, the portal is User-scoped, and administrative users needs to open Person-scoped forms and edit Entity rows attached to persons.

You can kind-sorta make the built-in widget work, but that's officially not how you're supposed to do it.

Instead, we will open an AJAX Popup/No Branding method usingย FW.Lazy.Popup(). The popup view contains a form embed code. Normally, FW.Lazy.Popup() automatically sizes and centers popups based on the size of the contents, but everything goes sideways when the contents change size after loading. Also, the embed code lazy-loads the form contents, which leads to a rapid, ugly sequence of stuff flashing and moving around.

Summary: Open the pop-up with opacity: 0, let it full load, center it, then reveal it. This works around the problem of FW.Lazy.Popup() not knowing the final size of the dialog from the start.

If you know a better way, we would be delighted to hear it!

The CSS opacity trick is unnecessary in many contexts. If your pop-up view doesn't contain an embedded form or other lazy-loaded content, you can simply let FW.Lazy.Popup() do its thing.

Pop-Up Method

Example Settings:

  • Type: GET
  • Output Type: AJAX Popup/No Branding
  • Action: edit_entity
  • View: Pop-Up: Edit Entity
  • Linked Query: Achievements

image.png

Pop-Up View

This is very simple: a single row with a static content block.

Notice that we're using merge fields likeย {{achievements[0].entity_id}}. Our calling method will pass in a single Entity GUID. From here, we'll populate as many fields like sys:entity:id:b4cd6c2f-b54c-43b8-978d-ec69932d5a50 as needed. I find this easier than passing parameters like form_sys:entity:id:b4cd6c2f-b54c-43b8-978d-ec69932d5a50 etc. from the caller.

Note also the popup=1 parameter. This is required if you'd like a title bar. ๐Ÿ™‚

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title></title>
  </head>
  <body>
    <div id="form_7f7a5f13-f1f6-4aa5-9de4-86596a194486">
      Loading...
    </div>
    <script async="async" src="/register/?id=7f7a5f13-f1f6-4aa5-9de4-86596a194486&amp;popup=1&amp;output=embed&amp;div=form_7f7a5f13-f1f6-4aa5-9de4-86596a194486&amp;person={{achievements[0].person_guid}}&amp;sys:entity:id:b4cd6c2f-b54c-43b8-978d-ec69932d5a50={{achievements[0].entity_id}}&amp;sys:entity:field:ach_name={{achievements[0].entity_name}}"></script>
  </body>
</html>

Achievements Query

This query outputs boring things like entity_id and entity_name. The Portal Identity filter is noteworthy: we either return all entity rows relevant to the logged-in User, or we return data for a single entity.

Our output node is achievements.

(
@entity_id is null
and /* Whatever is appropriate for authorizing the User */
)
or
e__JID_.[id] = @entity_id

Default View

Our calling view displays entity rows. When a row is clicked, our pop-up method is launched by FW.Lazy.Popup(). Notice that we pass in a CSS parameter to set the opacity to 0.

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title></title>
  </head>
  <body>
    <table class="table sortable">
      <colgroup>
        <col />
        <col />
        <col />
      </colgroup>
      <thead>
        <tr class="column">
          <th>Person GUID</th>
          <th>Entity GUID</th>
          <th>Achievement Name</th>
        </tr>
      </thead>
      <tbody>{% for item in achievements %}
        <tr>
          <td>{{item.person_id}}</td>
          <td>{{item.entity_id}}</td>
          <td><a data-href="?cmd=edit_entity&amp;entity_id={{item.entity_id}}" href="//" onclick="return (FW.Lazy.Popup(this, {css: {opacity: 0}}));">{{item.entity_name}}</a></td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </body>
</html>

Bonus Script: Reload the page when the dialog is submitted.

window.addEventListener can be vulnerable to XSS. The action taken here, reloading, isn't dangerous, but you should check the origin if you do something more adventurous.

// Listen for messages from FW.Lazy.Popup
window.addEventListener('message', function (event) {
  // If a form was submitted, reload the page
  if (event.data && event.data.xdm === true && event.data.query && event.data.query.cmd === 'submit') {
    window.location.reload();
  }
});

Form

The target form (7f7a5f13-f1f6-4aa5-9de4-86596a194486 in this example) should contain a hidden field for the entity GUID, like sys:entity:id:b4cd6c2f-b54c-43b8-978d-ec69932d5a50. If this field is correctly pre-filled with the Entity GUID, and theย &person= parameter is passed for the parent record, you can update one or more fields on the entity without creating new rows.

We recommend setting the entire form to Unsafe to avoid creating duplicate entity rows.

In the Custom Scripts section of the form, add the following. This is where the magic happens. We center the form, then unhide it.

FW.Dialog.Center();
$('.dialog_host > .dialog').css('opacity', 'unset');