...
Next, we need to change the add and delete actions in the view to support these new object return values (as JSON):
Code Block | ||
---|---|---|
| ||
<% def id = config.id ?: ui.randomId("patientIdentifiers") %> <script> function refreshPatientIdentifierTable(divId, patientId) { jq('#' + divId + '_table > tbody').empty(); jq.getJSON('${ ui.actionLink("getActiveIdentifiers", [returnFormat: "json"]) }', { patientId: patientId }, function(data) { publish(divId + "_table.show-data", data); }); } function refreshPatientIdentifiers${ id }(message, data) { if (data) publish("${ id }_table.show-data", data.activeIdentifiers); else refreshPatientIdentifierTable('${ id }', ${ patient.patientId }); } subscribe('${ id }.refresh', refreshPatientIdentifiers${ id }); subscribe('patient/${ patient.patientId }.changed', refreshPatientIdentifiers${ id }); subscribe('patient/${ patient.patientId }/identifiers.changed', refreshPatientIdentifiers${ id }); subscribe('${ id }_table.delete-button-clicked', function(message, data) { if (openmrsConfirm('${ ui.message("general.confirm") }')) { jq.post('${ ui.actionLink("deleteIdentifier") }', { returnFormat: 'json', patientIdentifierId: data }, function(data) { flashSuccess('${ ui.escapeJs(ui.message("PatientIdentifier.deleted")) }'); publish('patient/${ patient.patientId }/identifiers.changed', data); }, 'json') .error(function() { flashError("Programmer error: delete identifier failed"); }) } }); </script> <div id="${ id }"> ${ ui.includeFragment("widgets/table", [ id: id + "_table", columns: [ [ property: "identifierType", heading: ui.message("PatientIdentifier.identifierType") ], [ property: "identifier", userEntered: true, heading: ui.message("PatientIdentifier.identifier") ], [ property: "location", heading: ui.message("PatientIdentifier.location") ], [ actions: [ [ action: "event", icon: "delete24.png", tooltip: ui.message("PatientIdentifier.delete"), event: "delete-button-clicked", eventPrefix: id, property: "patientIdentifierId" ] ] ] ], rows: patient.activeIdentifiers, ifNoRowsMessage: ui.message("general.none") ]) } ${ ui.includeFragment("widgets/popupForm", [ id: id + "_add", buttonLabel: ui.message("general.add"), popupTitle: ui.message("PatientIdentifier.add"), fragment: "patientIdentifiers", action: "addIdentifier", submitLabel: ui.message("general.save"), cancelLabel: ui.message("general.cancel"), fields: [ [ hiddenInputName: "patientId", value: patient.patientId ], [ label: ui.message("PatientIdentifier.identifierType"), formFieldName: "identifierType", class: org.openmrs.PatientIdentifierType ], [ label: ui.message("PatientIdentifier.identifier"), formFieldName: "identifier", class: java.lang.String ], [ label: ui.message("PatientIdentifier.location"), formFieldName: "location", class: org.openmrs.Location ] ], successEvent: "patient/" + patient.patientId + "/identifiers.changed" ]) } </div> |
...
Step 9: Additional validation
TODO: don't let the user delete the last active identifierWe've actually missed a bit of necessary validation: we shouldn't blindly just delete the identifier without doing some checks first:
- we shouldn't delete an identifier that has already been deleted (in case the user is looking at an old version of the page)
- we shouldn't delete the last unvoided identifier
In case of any errors while executing a fragment action, you just need to return a FailureResult, and the UI framework will take care of sending it back in the correct way. (If the fragment is called via ajax, an errors object is returned as json or xml. If the fragment is done as a regular form submission, it is returned in a session attribute and displayed at the top of the page.) Typically fragment actions will declare a return type of FragmentActionResult, so they may return either a Success/Object result, or a FailureResult.
The simplest FailureResult constructor takes a single error message--that's the one we'll use here.
First, we add the validation to our fragment action method:
Code Block | ||
---|---|---|
| ||
/**
* Fragment Action for deleting an existing identifier
*/
public FragmentActionResult deleteIdentifier(UiUtils ui, @RequestParam("patientIdentifierId") Integer id,
@RequestParam(value="reason", defaultValue="user interface") String reason) {
PatientService ps = Context.getPatientService();
PatientIdentifier pid = ps.getPatientIdentifier(id);
// don't touch it if it's already deleted
if (!pid.isVoided())
return new FailureResult(ui.message("PatientIdentifier.delete.error.already"));
// don't delete the last active identifier
if (pid.getPatient().getActiveIdentifiers().size() == 1) {
return new FailureResult(ui.message("PatientIdentifier.delete.error.last"));
}
// otherwise, we go ahead and delete it
ps.voidPatientIdentifier(pid, reason);
return FragmentUtil.standardPatientObject(ui, pid.getPatient());
}
|
We also need to make one small change to the view in order to actually display those errors. Before we were flashing a generic message, that wasn't actually helpful to the user. Instead, we just pass the jQuery xml http response (it's the first argument that jQuery passes to the failure function for any ajax call) to a utility javascript method that the framework provides:
Code Block |
---|
subscribe('${ id }_table.delete-button-clicked', function(message, data) {
if (openmrsConfirm('${ ui.message("general.confirm") }')) {
jq.post('${ ui.actionLink("deleteIdentifier") }', { returnFormat: 'json', patientIdentifierId: data },
function(data) {
flashSuccess('${ ui.escapeJs(ui.message("PatientIdentifier.deleted")) }');
publish('patient/${ patient.patientId }/identifiers.changed', data);
}, 'json')
.error(function(xhr) {
fragmentActionError(xhr, "Programmer error: delete identifier failed");
})
}
});
|