Fragment Configuration in the Controller

The whole point behind fragments is to make them configurable. Each time you include a fragment, you can specify a FragmentConfiguration. So let's go ahead and make our fragment configurable in two ways:

  1. allow the date range to be overridden (we'll handle this in the controller)
  2. allow the columns to be chosen and reordered (we'll handle this in the view)

You may access fragment configuration properties in a controller method in either of two ways. If you declare a method parameter to be of type FragmentConfiguration, the framework will pass you the fragment's configuration. Or more conveniently, you can use the @FragmentParam annotation, which works just like Spring's @RequestParam.

So let's change the EncountersTodayFragmentController class as follows:

package org.openmrs.module.yourmoduleid.fragment.controller;

import org.openmrs.api.EncounterService;
import org.openmrs.ui.framework.annotation.FragmentParam;
import org.openmrs.ui.framework.annotation.SpringBean;
import org.openmrs.ui.framework.fragment.FragmentModel;

import java.util.Calendar;
import java.util.Date;

public class EncountersTodayFragmentController {

    private Date defaultStartDate() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    private Date defaultEndDate(Date startDate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(startDate);
        cal.add(Calendar.DAY_OF_MONTH, 1);
        cal.add(Calendar.MILLISECOND, -1);
        return cal.getTime();
    }

    public void controller(FragmentModel model,
                           @SpringBean("encounterService") EncounterService service,
                           @FragmentParam(value="start", required=false) Date startDate,
                           @FragmentParam(value="end", required=false) Date endDate) {

        if (startDate == null)
            startDate = defaultStartDate();
        if (endDate == null)
            endDate = defaultEndDate(startDate);

        model.addAttribute("encounters", service.getEncounters(null, null, startDate, endDate, null, null, null, null, null, false));
    }

}

As you see we are now getting startDate and endDate from the fragment configuration, but our original values as the defaults if they are not specified. So making this change will have no effect if you include the fragment without specifying config parameters, as we are currently doing in helloWorld.gsp. (At this point if you refresh http://localhost:8080/openmrs/yourmoduleid/helloWorld.page you will probably see no encounters, unless you have some today in your development database. Again, notice that the changes to the controller are picked up automatically. You don't need to restart anything. (In IntelliJ you do have to do Build -> Compile.)

Now, just so that we can see some data for example purposes, let's change the way we include the fragment on the helloWorld page so we get a day that does have encounters. Do something like this:

${ ui.includeFragment("uiframework", "welcomeMessage") }

${ ui.includeFragment("yourmoduleid", "encountersToday", [start: "2011-02-16", end: "2011-02-16 23:59:59.999"]) }

We are introducing another Groovy feature here. You can instantiate a Lists and Maps inline in your code.

["A", "List", "with", anotherObject] // this is a List
[x: 2, y: 8] // this is a Map, with (String) "x" -> (Integer) 2, and (String) "y" -> (Integer) 8

To include a fragment with custom configuration, you provide a third parameter to the includeFragment method, of type Map.

Note that we have given the dates in String format. The OpenMRS UI Framework is capable of converting between most types for you, using Spring's type converters. (Many are provided out of the box, but if you need to add your own, see Type Converters.) The UI Framework expects dates to be in "yyyy-MM-dd [HH:mm:ss][.SSS]" (i.e. year-month-day hour24:minute:second.millisecond, and neither the time-of-day or milliseconds are required), which differs from how the main OpenMRS web application does things.