How to trigger a Card Modal

You may want to create a card with modals that contain dynamic content received from a connector, so that your main card can display an overview, with additional details available within modals. In this guide we will illustrate this with a Recents card, that shows a count of recent messages and mentions, and two modals, one to show details of each.

The end goal will be a card like this:

1-modal-card-overview

Where clicking Messages or Mentions launches one of the two following modals, respectively:

2-modal-card-messages

3-modal-card-mentions

Returning data from the Connector

First we will cover how we should structure the data we return from the connector, in order to make it simpler for us to implement the card. Create a new connector, and add a service that returns some fake sample data we can use in the card.

Navigate to Development → Manage Spaces → Global → Connectors. Select Create Folder.

4-create-new-connector

In the Template drop-down, choose API Connector (custom authentication).

5-name-new-connector

Within this new folder, find proxy.js and select Edit.

6-edit-proxy-js

Now, replace the entire contents of the file with the following:

module.exports = async function (activity) {
  try {
    activity.Response.Data = {
      messages: {
        items: [
          {
            author: 'James',
            text: 'Did you get that report to Victor yet?',
            avatar: 'https://randomuser.me/api/portraits/men/76.jpg',
            time: '14m ago'
          },
          {
            author: 'Jane',
            text: 'We are meeting in the board room at 1pm to discuss the agenda for next week',
            avatar: 'https://randomuser.me/api/portraits/women/44.jpg',
            time: '2h ago'
          },
          {
            author: 'Victor',
            text: 'If you can get that report on my desk by tomorrow, we will both be closer to that bonus!',
            avatar: 'https://randomuser.me/api/portraits/men/74.jpg',
            time: '1d ago'
          }
        ]
      },
      mentions: {
        items: [
          {
            author: 'Sarah',
            text: 'Hey <span class="blue">@Ed</span>, wanna grab lunch with me today?',
            avatar: 'https://randomuser.me/api/portraits/women/73.jpg',
            time: '2m ago'
          }
        ]
      }
    };
  } catch (error) {
    // return error response
    var m = error.message;
    if (error.stack) m = m + ": " + error.stack;

    activity.Response.ErrorCode = (error.response && error.response.statusCode) || 500;
    activity.Response.Data = { ErrorText: m };
  }
};

Here, we have set the model returned by the connector, which is the contents of activity.Response.Data. We've populated it with two objects, messages and mentions. Each contains an array items with fake message objects within them. Within the card and modals, we will now render this data we have returned from the connector.

Finally, we need to create the connector content item. Navigate to Content Manager → Service Connectors, and select Create New Connector.

7-create-connector-item

In the next screen, simply provide a name and title, and in the Connector Type drop-down, select the connector we have just made. The name will match the name of the connector folder you created. Press Save to finish.

Rendering data in the Card and modals

Navigate to Development → Manage Spaces → Global → Components and set Template Type to Card, Template to Basic Card (with test data).

Under the Service section, set Service Type to Service Connectors and Connector to the connector we have created. In this case, it will match the title of the connector content item created in the previous step.

Finally, set Service to API Proxy (proxy) and select Create. We can leave the Proxy URL field blank.

Launching modals from a Liquid view

In the component folder we have created, select Designer on default.card, and select the content element. Scroll down until you see the view field on the left, and paste in the following Liquid markup:

<div class="layout-vertical">

  <div class="font-headline layout-self-center">Recent Activity</div>

  <div class="layout-horizontal layout-around-justified mx my">

      <div class="layout-vertical layout-center launch-modal" tap-event="dialog" tap-event-data="messages" dialog-title="Recent Messages">
        <div class="layout-center-justified">
          <div class="modal-badge font-headline">{{ messages.items.length }}</div>
        </div>
        <div class="layout-center text-secondary">Messages</div>
      </div>

      <div class="layout-vertical layout-center launch-modal" tap-event="dialog" tap-event-data="mentions" dialog-title="Recent Mentions">
        <div class="layout-center-justified">
          <div class="modal-badge font-headline">{{ mentions.items.length }}</div>
        </div>
        <div class="layout-center text-secondary">Mentions</div>
      </div>

  </div>

</div>

<style>
  modal-badge {
    width: 64px;
    height: 64px;
  }

  .launch-modal {
    cursor: pointer;
  }
</style>

Here, we've created the main Card view, which has a title, and two clickable divs. The divs display the count of messages or mentions along with a label, and will launch the modal upon click.

To launch modals, the following attributes are used on the div that encloses the clickable area:

tap-event="dialog" tap-event-data="messages" dialog-title="Recent Messages"
tap-event="dialog" tap-event-data="mentions" dialog-title="Recent Mentions"

These attributes specify the following: tap-event="dialog" sets the desired action to open a modal, tap-event-data is then used to provide the name of the modal card to open. For example, tap-event-data="messages" will open, as a modal, a file messages.card from the component folder. Finally dialog-title simply sets the title to display on the modal when opened.

Creating modal .card files

As yet, we don't actually have the messages.card and mentions.card that the launch attributes are referencing. We will now create those, and their markup for rendering our data, as the final step. Navigate back to the folder containing your default.card, and select New File. Select the card type with the empty card template and name it messages.

8-modal-card-create

Repeat this process again to create a new card, but named mentions.

Now, back in your component's folder, select Designer on the message.card file you've created. Add a Liquid View element:

9-add-liquid-view-element

Next is the most important step, to set the modelRoot property of the liquid view, to the part of the model we want to render in this modal. As this is messages.card, we want to set it to state.model.messages. This means that when we refer to items in the view, we are referring to the items attached to the messages key.

10-set-model-root

Finally, scroll down to the view field, and paste in the following Liquid markup:

{% for message in items %}
  <div class="layout-horizontal m">
    <div class="layout-self-start mlsm">
      <iron-image sizing="cover" preload class="avatar layout-self-start mr" src="{{ message.avatar }}"></iron-image>
    </div>
    <div class="layout-flex text-primary">
        <div class="layout-horizontal layout-around-justified layout-inline">
          <div class="font-bold" style="width:100%">{{ message.author }}</div> 
          <div style="width:100%;text-align:right;">{{ message.time }}</div>
        </div>
        <div class="font-body1 mtsm mbsm">
          <span class="grey">{{ message.text }}</span>
        </div>
    </div>
  </div>
{% endfor %}

<style>
.avatar {
  border-radius: 50%;
  width: 48px;
  height: 48px;
}
</style>

We can now repeat this exact same process with mentions.card - except this time set modelRoot of your Liquid view to state.model.mentions. We can even use the exact same Liquid markup from above, as items now simply refers to mentions instead of messages. The item structures are identical, so it will simply loop through and render our mentions instead of messages.

After this, enter the Digital Assistant UI, and add the Card you've created to the Workplace. View the Card, and you should see the results from the beginning of the article!