My Favorite Patients MVC Example

The Goal

We're going to add a "My Favorite Patients" feature in our module. We'll add a link in the OpenMRS header that says "My Favorite Patients" and that page will show a list of the currently logged-in user's favorite patients. We will store these using the existing "Cohort" object, rather than creating our own domain object:

Initial Setup

We're going to do this within the jspexample module. If we were doing this for real, we'd create a new module, and add the webModuleApplicationContext.xml file as described here

Create a page to show My Favorite Patients

Note that we're going to delegate the actual work to a MyFavoritePatientUtil class. In real life we would make a service for this.

@Controller
class MyFavoritePatientsController {

	@RequestMapping("/module/jspexample/myfavoritepatients")
	public void showFavoritePatients(ModelMap model) {
		model.put("favorites", MyFavoritePatientUtil.getFavoritePatients(Context.getAuthenticatedUser()));
		model.put("me", Context.getAuthenticatedUser());
	}
}

Next we need a JSP to display this, in the standard location of /web/module/myfavoritepatients.jsp:

<%@ include file="/WEB-INF/template/include.jsp" %>

<%@ include file="/WEB-INF/template/header.jsp" %>

<h1>My ${fn:length(favorites)} favorite patient(s) (for ${me.username})</h1>

<ul>
	<c:forEach var="p" items="${favorites}">
		<li>
			${p.personName}
			<c:choose>
				<c:when test="${p.gender == 'F'}">
					Female
				</c:when>
				<c:otherwise>
					Male
				</c:otherwise>
			</c:choose>
		</li>
	</c:forEach>
</ul>

<%@ include file="/WEB-INF/template/footer.jsp" %>

The only problem is that this list is empty. Let's make a hacky form that allows us to add a patient

A hacky way to add new favorite patients

First we add this to the JSP page

<form method="post" action="addFavoritePatient.form">
	Add a patient by primary key: <input type="text" name="patient_id"/>
	<input type="submit" value="Add"/>
</form>

Better widget for picking a Patient

<form method="post" action="addFavoritePatient.form">
	Add a patient <openmrs:fieldGen formFieldName="patient_id" type="org.openmrs.Patient"/>
	<input type="submit" value="Add"/>
</form>

Now we need a method on the controller to handle that submission.

  • You can use the @RequestParam annotation to have Spring automatically bind a parameter in the request to an argument to your method. It automatically converts from String (which all HTTP request parameters are by nature) to the specified class.
  • If you return a String that starts with "redirect:" then Spring MVC does the appropriate response.setRedirect(...) call for you.
    @Controller
    public class MyFavoritePatientsController {
    	...
    
    	@RequestMapping("module/jspexample/addFavoritePatient")
    	public String addFavoritePatient(@RequestParam("patient_id") Integer patientId) {
    		Patient p = Context.getPatientService().getPatient(patientId);
    		if (p != null)
    			MyFavoritePatientUtil.addFavoritePatient(Context.getAuthenticatedUser(), p);
    		return "redirect:myfavoritepatients.form";
    	}
    }
    

There's a nicer way to do this, though. OpenMRS provides a bunch of editor classes that know how to convert between a String (in an HTTP request) and our domain objects. In this case org.openmrs.propertyeditor.PatientEditor.

By default, Spring will only automatically convert a small number of parameter types that it knows: Integers, Doubles, Booleans, and a few more. In our controller we need to tell it about other types it should automatically convert:

@Controller
public class MyFavoritePatientsController {
	@InitBinder
	public void initBinder(WebDataBinder wdb) {
		wdb.registerCustomEditor(Patient.class, new PatientEditor());
	}
	...

	@RequestMapping("module/jspexample/addFavoritePatient")
	public String addFavoritePatient(@RequestParam(value="patient_id", required=false) Patient patient) {
		if (patient != null)
			MyFavoritePatientUtil.addFavoritePatient(Context.getAuthenticatedUser(), patient);
		return "redirect:myfavoritepatients.form";
	}
}

MyFavoritePatientsUtil

public class MyFavoritePatientsUtil {

	private static String cohortName(User u) {
		return "Favorite Patients for User " + u.getUserId();
	}
	
	private static List<Patient> getPatients(Cohort cohort) {
		if (cohort == null)
			return new ArrayList<Patient>();
		else
			return Context.getPatientSetService().getPatients(cohort.getMemberIds());
	}
	
	public static List<Patient> getFavoritePatients(User u) {
		Cohort c = Context.getCohortService().getCohort(cohortName(u));
		return getPatients(c);
	}
	
	public static void addFavoritePatient(User u, Patient p) {
		Cohort c = Context.getCohortService().getCohort(cohortName(u));
		if (c == null) {
			c = new Cohort();
			c.setName(cohortName(u));
			c.setDescription("Automatically generated");
		}
		c.addMember(p.getPatientId());
		Context.getCohortService().saveCohort(c);
	}
	
}