Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Warning

This step of the tutorial needs to be updated, but I can't really do that until I include the infobox fragment in a uiwidgets module.

You won't be able to follow this as currently written.

Also, FWIW, I'm considering switching the event bus mechanism either to something simpler (just using jQuery), or to something more sophisticated using (e.g. backbone.js). Opinions welcome!

This tutorial shows the way to do things as of April 10, 2012.

The fact that we are writing reusable page fragments, rather than whole pages, is very powerful, and will lead to lots of flexibility and code reuse across the OpenMRS ecosystem. But it also means we need to introduce a new (more complicated) paradigm for having fragments communicate with each other. For example, we've just built a fragment that shows a list of encounters. OpenMRS also The UI Library module includes a fragment that can use ajax to load a preview of an encounter that you select on a page. Clearly, these two fragments are very useful when combined. But at the same time, neither one of them should depend on the other, since they are both perfectly functional alone, and they can both be used with other fragments too. So in order to make our fragments as flexible and reusable as possible, we need to "decouple" them.

The UI Framework includes a mechanism (based on the javascript event bus PageBus) that allows fragments to publish and subscribe to messages, and pass content with those messages.

First, you need to download and install version 1.0 of the UI Library module from the OpenMRS module repository. (I make no promises that the widgets it contains won't change in later module versions.)

To demonstrate that functionality, we are going augment our encountersToday fragment so that it publishes an "encounterSelected" message when you click on one of the listed encounters. Later we'll play around with a couple things we can do with this message.We .

Edit yourmodule/omod/src/main/webapp/pages/helloWorld.gsp to add one line to the top:

Code Block

<% ui.decorateWith("standardPage") %>

This decorator is provided by the UI Library module, and it includes standard js and css that the widgets provided by the module depend on.

Now, we can edit the encountersToday.gsp fragment (omitting the refresh button and ajax for simplicity)

Code Block
<%
    def id = config.id ?: ui.randomId("encountersToday")
    def props = config.properties ?: ["encounterType", "encounterDatetime", "location", "provider"]
    def showSelectButton = config.showSelectButton ?: false
%>

<table id="${ id }" class="decorated">
    <thead>
        <tr>
            <% if (showSelectButton) { %>
                <th></th>
            <% } %>
            <% props.each { %>
                <th>${ ui.message("Encounter." + it) }</th>
            <% } %>
        </tr>
    </thead>
    <tbody>
        <% if (encounters) { %>
            <% encounters.each { enc -> %>
                <tr>
                    <% if (showSelectButton) { %>
                        <td>
                            <a href="javascript:publish('${ id }.encounterSelected', ${ enc.id })">
                                <img src="${ ui.resourceLink("uiframework", "images/info_16.png") }"/>
                            </a>
                        </td>
                    <% } %>
                    <% props.each { prop -> %>
                        <td><%= ui.format(enc."${prop}") %></td>
                    <% } %>
                </tr>
            <% } %>
        <% } else { %>
            <tr>
                <td colspan="4">${ ui.message("general.none") }</td>
            </tr>
        <% } %>
    </tbody>
</table>

...

Also notice the usage of the "ui.resourceLink(String provider, String path)" method, which allows us to include anything in the images, scripts, and styles folders of the web application.reference content that modules provide in their resources folder. (In this case we are referring to a resource provided by a specific module, which is good style, but if we'd done ui.resourceLink("images/info_16.png"), that would search through all modules until it finds a matching resource.)

We're following good practices here and trying to make our fragment reusable. It can have a select button, but it doesn't require one. In reality we'd want to go further and allow the consumer of the fragment to specify the icon to use, etc.

...

To see this in action, try refreshing http://localhost:8080/openmrs/openmrs2pages/helloWorld.page and click on one of the info icons. You'll get a 404 error, because we have not actually written an encounter page in this tutorial, but you get the idea...

Now, let's do something a bit different. On the helloWorld page, let's also include the "infobox" fragment, and connect it to our encountersToday fragment such that selecting an encounter show a preview of it on our current page, via ajax.

Code Block
<% ui.decorateWith("standardPage") %>

${ ui.includeFragment("welcomeMessage") }

<table width="100%">
    <tr valign="top">
        <td width="65%">
            ${ ui.includeFragment("encountersToday",
                    [   start: "2011-02-16",
                        end: "2011-02-16 23:59:59.999",
                        properties: ["location", "encounterDatetime"],
                        decorator: "widget",
                        decoratorConfig: [title: "Today's Encounters"],
                        id: 'encsToday',
                        showSelectButton: true
                    ]) }
        </td>
        <td width="35%">
            ${ ui.includeFragment("infoBoxinfobox", [id: "infoBoxpreviewBox"]) }
        </td>
    </tr>
</table>

<script>
    jq(function() {
        subscribe('encsToday.encounterSelected', function(topic, encId) {
            infoBoxpreviewBox.showEncounterByIdshowEncounter(encId)
        });
    });
</script>

We've included a second fragment (which is provided by the UI Library module), next to our encountersToday one (using a table to do a quick hacky layout). And we've changed the javascript callback for the encounterSelected event to call a method on the infoBox infobox fragment (note that that's the id we included it with).

The key point here is that we've written a fragment that we were able to reuse for two different behaviors (go to encounter page, and preview encounter) without changing the fragment itself, but just by wiring it up to different actions. As we If you follow these patterns while building the OpenMRS 2.x Reference Application, we'll new application functionality, you will also build up a reusable library of fragments that will allow module developers to build functionality very rapidly (imagine you can use to assemble future UIs even more quickly. (Imagine writing a page to quickly collect weights and heights by wiring up a find-patient widget, to an obs-table widget with a form widget.) . And the The same pattern will eventually let system administrators compose pages out of fragments that can be wired together using a (not-yet-ready) XML format.