1 / 28

OpenMRS 2.0 UI Framework Options

OpenMRS 2.0 UI Framework Options. OpenMRS 2.0 UI Redesign. As we rewrite the UI, we have a one-time opportunity to pick a new web framework All options are on the table Criteria Plug in our existing Java API Support extensions via modules Allow us to build reusable components

hogan
Télécharger la présentation

OpenMRS 2.0 UI Framework Options

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. OpenMRS 2.0 UI Framework Options

  2. OpenMRS 2.0 UI Redesign • As we rewrite the UI, we have a one-time opportunity to pick a new web framework • All options are on the table • Criteria • Plug in our existing Java API • Support extensions via modules • Allow us to build reusable components • Easy! Fun! Beautiful! • Accessible to entry-level coders • Well-supported

  3. “All” options are on the table • Spring 3 • Spring MVC + JQuery + roll-our-own widgets • Spring MVC + JQuery + StringTemplate • JSF + ICEFaces • Ruby on Rails (via JRuby) • JQuery • Grails • YUI • JQuery • GXT

  4. Spring 3 • “the leading platform to build and run enterprise Java applications. Led and sustained by SpringSource, Spring delivers significant benefits for many projects, increasing development productivity and runtime performance while improving test coverage and application quality” • Why? • We use it, we like it • Extremely flexible • Heavily used, and getting better all the time

  5. Spring 3+Spring MVC+JSP+JQuery • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/spring3mvc+jquery • Thanks Harsha! • The modern version of what we're doing now • Maven! Jetty! • Spring 3 MVC has lots of timesaving features we haven’t taken advantage of • Jquery is powerful and we haven't taken advantage • Consciously build reusable UI widgets

  6. Spring 3+Spring MVC+JSP+JQuery @Controller publicclass PatientController { @RequestMapping(value="/patient", method=RequestMethod.GET) publicString viewPatient(@RequestParam("patientId") Patient patient, Model model) { model.addAttribute(patient); if (patient != null) model.addAttribute("encounters", Context.getEncounterService().getEncountersByPatient(patient)); } } <%@attributename="encounterList"required="true” type="java.util.List"%> ... <scripttype="text/javascript"> $(document).ready(function() { $('#encounters').dataTable(); }); </script> <tableid="encounters"border="1"width="500"cellspacing="0"> <thead> <tr> <th>Encounter Id</th> <th>Encounter Date</th> <th>Encounter Patient</th> </tr> </thead> <tbody> <c:forEachvar="encounter"begin="0"items="${encounterList}"> <trclass="gradeX"> <td>${encounter.encounterId}</td> <td>${encounter.encounterDatetime}</td> <td>${encounter.patientId}</td> </tr> </c:forEach> </tbody> </table> <scripttype="text/javascript"> $(function() { $('#tabs').tabs(); }); </script> ... <divid="tabs"> <ul> <li><ahref="#tabs-1">Patient</a></li> <li><ahref="#tabs-2">Encounters</a></li> </ul> ... <divid="tabs-2"> <h1>Patient Encounters</h1> <openmrs:encounters encounterList="${encounters}"/> </div>

  7. Spring 3+Spring MVC+JSP+JQuery • Pros: • Flexibility (never “not be able to do” something) • Strong community • Configured right, it allows RAD • No learning curve for existing devs • Cons: • Significant learning curve for newbies (lots of technologies, and they are not simplified) • Open questions: • Can we leverage Hibernate annotations?

  8. ... + Spring MVC + StringTemplate • http://github.com/diptanu/openmrs-beta/tree/module • -Thanks Thoughtworks! • “[StringTemplate's] distinguishing characteristic is that it strictly enforces model-view separation unlike other engines. Strict separation makes websites and code generators more flexible and maintainable.” • Controllers like you're used • StringTemplate instead of JSP • Recommended by Thoughtworks developers • Templates allow for page layouts and reusable components

  9. ... + Spring MVC + StringTemplate layout/layout.st <html> <head> <title>Patient Dashboard</title> <link rel="stylesheet" href="../../../styles/jquery.ui.all.css" type="text/css"> <script type="text/javascript" src="../../../scripts/jquery-1.4.2.js"></script> <script type="text/javascript" src="../../../scripts/jquery-ui-1.8.2.custom.min.js"></script> <script type="text/javascript" src="../../../scripts/jquery.dataTables.js"></script> </head> <body> <div class="header">$partials/header()$</div> <div class="content">$body$</div> <div class="footer">$partials/footer()$</div> </body> </html> widgets/encounters.st $if(encounters)$ <script type="text/javascript"> jQuery(function() { jQuery('#encounters').dataTable(); }); </script> <table id="encounters" border="1" width="500" cellspacing="0"> <thead> <tr> <th>Date</th> <th>Location</th> <th>Type</th> <th>Provider</th> </tr> </thead> <tbody> $encounters:{ encounter | <tr class="gradeX"> <td>$encounter.encounterDatetime$</td> <td>$encounter.location.name$</td> <td>$encounter.encounterType.name$</td> <td>$encounter.provider.givenName$</td> </tr> }$ </tbody> </table> $else$ <b>There are currently no encounters</b> $endif$ patientdashboard.st <div id="tabs"> <ul> <li><a href="#tabs-1">Patient</a></li> <li><a href="#tabs-2">Encounters</a></li> $menuext:{item| <li><a href="#tabs-3">$item$</a></li> }$ </ul> ... <div id="tabs-2"> $widgets/encounters(encounters=patient.encounters)$ </div> ...

  10. ... + Spring MVC + StringTemplate • Pros: • Strict Model-View separation will make us program better • Cons: • Unclear whether this approach has significant advantages over more-commonly-used technologies • Open Questions: • When a module attaches to an extension point, that now becomes view-only, and can’t add any data to the model. Right?

  11. Spring 3 + JSF + ICEFaces • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/jsf+icefaces/ • Thanks Shazin! (ICEFaces 1.8, JSF 1.2, no Spring) • JSF 2 with Spring in progress • JSF: Developed through the Java Community Process under JSR - 314, JavaServer Faces technology establishes the standard for building server-side user interfaces. With the contributions of the expert group, the JavaServer Faces APIs are being designed so that they can be leveraged by tools that will make web application development even easier • ICEFaces: J2EE Ajax framework for developing and deploying rich enterprise applications (REAs). With ICEfaces, enterprise Java developers can easily develop rich enterprise applications in Java, not JavaScript • JSF instead of Spring MVC • Component-based • Server-side state, stored in the session • Facelets instead of JSP • No Javascript required! JSF automatically reloads page fragments as required

  12. Spring 3 + JSF + ICEFaces // JSF 1.2 with XML-defined beans publicclass TableBean implements PageBean { private List headers; private List<EncounterDTO> body; public List<EncounterDTO> getBody() { returnbody; } publicvoid setBody(List<EncounterDTO> body) { this.body = body; } public List getHeaders() { returnheaders; } publicvoid setHeaders(List headers) { this.headers = headers; } } ... <ice:form> <ice:hiddenTextbinding="#{patient.init}" /> <ice:panelTabSet> ... <ice:panelTabid="encounters"label="#{msgs['encounters.tab.label']}"> <ice:panelGridid="panelGrid2"> ... <ice:dataTablevalue="#{table.body}" var="item"> <ice:column> <f:facetname="header"> <ice:outputTextvalue="#{msgs['encounters.date.label']}"/> </f:facet> <ice:outputTextvalue="#{item.date}"/> </ice:column> <ice:column> <f:facetname="header"> <ice:outputTextvalue="#{msgs['encounters.location.label']}"/> </f:facet> <ice:outputTextvalue="#{item.location}"/> </ice:column> ... </ice:dataTable> </ice:panelGrid> </ice:panelTab> </ice:panelTabSet> </ice:form> ... // JSF 2.0 with Spring-managed beans @Component("helloWorld") @Scope("session") publicclass HelloWorldBean { public String getMessage() { return"Hello World!"; } publicUser getAuthenticatedUser() { returnContext.getAuthenticatedUser(); } }

  13. Spring 3 + JSF + ICEFaces • Pros: • JSF is the J2EE 6 Standard • ICEFaceslooks good • Cons: • Steep learning curve • Poor RAD (editing a single server-side component requires restarting Jetty) • Open Questions: • How will module extensions work in a component-based framework?

  14. Ruby on Rails (via JRuby) • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/jruby+rails+jquery/ • “Ruby on Rails is an open-source web framework that’s optimized for programmer happiness and sustainable productivity. It lets you write beautiful code by favoring convention over configuration” • “A domain-specific-language for database-backed web applications” • Thoughtworks CTO recommends Java backends with RoR-via-JRuby front-ends

  15. Ruby on Rails (via JRuby) class PatientController < ApplicationController def view @patient = Context.patientService.getPatient(params[:id].to_i) if @patient.nil? puts "No patient found!" raise "No patient found!" end @encounters = Context.encounterService.getEncountersByPatient(@patient) # record that this user viewed this patient PatientViewed.record_view($omrs.authenticated_user, @patient) end def find ret = []; $omrs.patient_service.getPatients(params[:query]).each do |patient| ret << helpers.convert_to_dto(patient) end render :json => ret end end • Conventions for project layout • app • controllers • helpers • models • views • layouts • (controller) • (action) • config • environments • initializers • locales • public • images • javascripts • stylesheets <script type="text/javascript"> $(document).ready(function() { $("#tabs").tabs(); }); </script> <h1><%= format @patient %> - <%= format @patient.patient_identifier%></h1> … <div id="tabs-2"> <%= render :partial => "widgets/encounter_list", :locals => { :encounters => @encounters } %> </div>

  16. Ruby on Rails (via JRuby) • ActiveRecordand Migrations make it very easy to create (application-level) database-backed functionality • jruby script/generate model PatientVieweduser_id:intpatient_id:int The “domain object” doesn’t declare any properties, because those are implied by the database table class PatientViewed < ActiveRecord::Base # get recent PatientVieweds for the given user def self.recently_viewed(user_id, n=5) all(:conditions => ["user_id = ?", user_id], :order => "created_at DESC", :limit => n ) end # get ids of patients recently viewed by the current user def self.recently_viewed_ids(user_id, n=5) recently_viewed(user_id, n).collect { |pv| pv.patient_id } end # record that a User viewed a Patient def self.record_view(openmrsuser, openmrspatient) destroy_all({ :user_id => openmrsuser.user_id, :patient_id => openmrspatient.patient_id }) create({ :user_id => openmrsuser.user_id, :patient_id => openmrspatient.patient_id }) end end This migration is automatically created class CreatePatientVieweds < ActiveRecord::Migration def self.up create_table :patient_vieweds do |t| t.integer :user_id t.integer :patient_id t.timestamps end end def self.down drop_table :patient_vieweds end end

  17. Ruby on Rails (via JRuby) • Pros: • It’s Fun! • Excellent RAD • Mongrel + Rails seems to be better than Jetty +Spring for this • ActiveRecord and migrations • Cons: • Rails session != Java session => need to write our own session management • No idea of Services • Domain objects are supposed to be intelligent • Wouldn’t be able to use the full power of the framework, because of our dumb domain objects. • Potential for some annoying incompatibility to hit us out of the blue • Open Questions: • How would module extensions work? Would an omod contain Java code *and* Ruby code? (Yuck.) • How would Ruby migrations work with Liquibase? • How good is Eclipse integration?

  18. Tangent: reusable components in RoR • From views/widgets/_patient_search.html.erb • ... • <script type="text/javascript"> • var options = { • clickUrl: function(rowNum, item) { • return "/patient/view/" + item.patient_id; • }, • icon: function(rowNum, item) { • return '<imgsrc="/images/' + (item.gender == 'M' ? 'male' : 'female') + '.png"/>'; • }, • … • }; • $(document).ready(function() { • $('#<%= id %>_form').submit(function() { • $.getJSON("<%= patient_search_opts.search_url %>", • { query: $('#<%= id %>_query').val() }, • <%= id %>_results_results_callback(options) • ); • return false; • }) • }); • </script> • <form id="<%= id %>_form"> • ID or Name: <input id="<%= id %>_query" type="text"/> <input type="submit" id="<%= id %>_button"/> • </form> • <%= render :partial => "widgets/vertical_panel", • :locals => { :id => "#{id}_results", :options => patient_search_opts } %>

  19. Grails • “GRAILS: the search is over…Have your next Web 2.0 project done in weeks instead of months. Grails delivers a new age of Java web application productivity.” • Attempts to replicate the magic of RoR in the Java world (used to be called “Groovy on Rails” until RoR asked them to stop) • Uses Spring, Hibernate, SiteMesh, Jetty, … • Groovy • Java + dynamic typing + closures + compiler magic • 99.9% of legal Java is legal Groovy def results = Context.patientService.getPatients(“darius”).collect { [ ptId: it.patientId, name: it.personName.toString() ] }

  20. Grails + JQuery • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/grails-poc-dj/ class PatientController { def view = { def p = Context.patientService.getPatient(params.int['patientId']); [ patient: p, encounters: Context.encounterService.getEncountersByPatient(p) ] } def searchJson = { List<Patient> pats = Context.getPatientService().getPatients(params['query']); def output = pats.collect { [ patientId: it.patientId, age: it.age, gender: it.gender, name: it.personName.toString() ] } render output as JSON } } class OpenmrsTagLib { static namespace = 'openmrs' def format = { attrs -> def obj = attrs['object'] f (obj != null) { if (objinstanceof User) { out << "${obj.username} (${formatName(obj.person)})“ } else { out << "${obj} (don't know how to handle ${obj.class})" } } String ajaxSearchHelper(Map attrs) { • … return """ <form id="${id}_form“> <input id="${id}" type="text" size="40"/> <input type="submit" id="${id}_button" value="Search"/> </form> <div id="${id}_results" class="vertical-panel"></div> <script type="text/javascript“> ${createDecoratorIfNecessary} \$(document).ready(function() { \$("#${id}_form").submit(function() { … """ }

  21. Grails + Grails-UI (YUI) • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/grails-poc/ • Thanks Harsha! • <gui:tabView id="tabView"> • … • <gui:tab id="t1" label="Encounters"> • <h2>Patient Encounters</h2> • <gui:dataTable • draggableColumns="true“ • columnDefs="[ • [date:'Date', sortable:true, resizeable: true], • [location:'Location', sortable:true, resizeable: true], • [type:'Type', sortable:true, resizeable: true], • [provider:'Provider', sortable:true, resizeable: true]]“ • sortedBy='date‘ • controller="patient” • action="patientEncounters“ • params="[id:'${model.id}']" • caption="click on a row, and it will expand" • paginate="true" • rowExpansion="false" • rowsPerPage="20" • totalRecordsKey="meTotalRecs" • /> • </gui:tab> • <openmrs:extensionPoint • pointId="org.openmrs.patientDashboardTab" type="html"> • … • </openmrs:extensionPoint> • … • </gui:tabView> <head> <meta name="layout" content="openmrs"/> </head> <body> <h1>Find a patient</h1> <g:form controller=“patient” action=“find"> ID or Name: <g:textField name="id”/> <g:submitButton name="submit" value=“Search"/> </g:form> … <body> It’s unlikely that we’d choose YUI over Jquery, even if it does come in the Grails-UI plugin…

  22. Grails • Pros: • It’s Fun! • Almost no learning curve for current developers • RAD almost as good as in Rails • GORM (analog of ActiveRecord) • Jetty (needs to restart more often than Mongrel) • Sitemesh handles page templates • Auto-recompiling groovy taglibs are great for pulling repeated code out of pages • Cons: • Small community, possible lack of future support • IDE integration is surprisingly mediocre • A couple times I’ve read: “we lost almost all the productivity gains Grails gave us because of undocumented bugs in Grails”. (Personally Google searching has found me answers for all errors I’ve searched for.) • Open Questions: • Can we use the embedded database to allow you to develop without even having a MySQLdatabase? • Is Spring Roo worth looking into as an alternative?

  23. GXT (EXT on GWT) • http://svn.openmrs.org/openmrs-contrib/ui-frameworks/spring3+gxt/ -Thanks Sy! • GWT is a development toolkit for building and optimizing complex browser-based applications • Ext JS is a cross-browser JavaScript library for building rich internet applications. Ext JS features high performance, customizable UI widgets and a well-designed and extensible component model. • => Ext GWT is a Java library for building rich internet applications with Google Web Toolkit (GWT) • You only write Java! GWT produces HTML, Javascript, AJAX, … • “Rich Internet Application” instead of “a webapp”

  24. GXT (EXT on GWT) • Client UI code looks like a Swing app • public class MainContent extends LayoutContainer{ • … • public void openPatientDashboard(PatientDTO patient) { • removeAll(); • add(new PatientDashboard(patient)); • layout(); • } • } • public class PatientDashboard extends LayoutContainer { • … • @Override • protected void onRender(Element parent, int pos) { • super.onRender(parent, pos); • setLayout(new RowLayout(Orientation.VERTICAL)); • … • TabItemdemographics = new TabItem("Demographics"); • demographics.add(createDemographicsForm()); • TabItem encounters = new TabItem("Encounters"); • encounters.add(new PatientEncounterPanel2(patient)); • tabs = new TabPanel(); • tabs.add(demographics); • tabs.add(encounters); • … • } You need lots of (formulaic) classes: (client) interface PatientService interface PatientServiceAsync (common) class PatientDTO (server) class PatientServiceImpl DTOs, for sending data between client and server package web.openmrs.common; … public class EncounterDTOimplements Serializable { private Integer id; private Date date; private String location; private String type; private String provider; private List<ObsDTO> obs; … }

  25. GXT (EXT on GWT) • This would be a completely different sort of application

  26. GXT (EXT on GWT) • Pros: • No HTML or Javascript! • “Since this is Java, the API and JavaDocs are available in the IDE as context sensitive help and the source code is available to look at instead of referring to (outdated) webpages, also has a large library of examples and showcase” • Cons: • Have to build a set of DTOs and services specifically for GWT • Steep(?) learning curve for existing and newbie devs • “the initial load to the client's browser is fairly large but a good framework for loading is in place. the plus side to this is once it is loaded, the page never has to be loaded again so page loads do not exist, only data calls (reducing input/output over the wire as well as server load)” • Open Questions: • Could modules plug in, given the client-server architecture?

  27. Still To Investigate • More review of JSF 2 + ICEFaces 2 • Is GXT even an option? • Can it support modules? • Can we use an embedded DB in development mode with any of these technologies? • Can Hibernate Annotations provide some of the functionality of ActiveRecord/GORM in Spring-3-based technologies? • JSR-286 Portlets

More Related