Hitachi Vantara Pentaho Community Wiki
Child pages
  • Advanced Guide to MVC in Pentaho XUL Applications
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 33 Next »

MVC in Pentaho XUL applications can be acheived by architecting your application using the pattern we will detail here.  As a refresher, the purpose of the MVC pattern is separation of concerns in UI applications.  The idea is to keep a clear separation between the data we would like to display and how that data is being displayed.  In Pentaho XUL framework (PXF) the view layer is represented at least partially represented in a xul dom object (typically coming from a xul file).  I say "partially" because you could consider view logic to be part of the view and view logic is not included in the XUL DOM

The diagram below shows how Pentaho XUL MVC would operate in an example UI widget project Foo.  Starting from the left we see that the XUL file foo.xul is represented in your application as a XulDom.  The XulDom is what we consider to be the View of our MVC implementation  PXF by way of the XulDom is responsible for instantiating and laying out UI components, however, as we mentioned earlier view logic is not handled by the XulDom.  The responsiblity for handling view logic rests on the Controller and can be thought of as being component-state changes or non-component-state events. These two classes of events are handled differently in the Controller.  Events are handled directly in the controller whereas component state changes are simply mapped in the controller, leaving the handling of state changes to the binding layer.  By structuring your application this way, you will have zero dependencies on Xul inside your Model which makes your model much more testable.  Only your Controller code will know anything about Xul in the case that it needs to launch XulDialogs for example.  Your Controller, however, should never need to reference a XulComponent directly.

Data Binding and Component-state Events

It is the responsibility of your Controller implementation to setup bindings between your XUL components and your data model (a POJO/Java Bean). What do we mean by "binding"? Binding is a way to associate bean properties so that when one changes, the other is kept in sync. Binding can be unidirectional or bidirectional, but we won't get into that here.  For more information on Pentaho XUL binding see XUL Bindings. We will assume that a binding is always bidirectional meaning when either side of the binding changes, the other will reflect the change. This is useful in UI applications because we can tie, say, a text box's value to a model bean String property.  Consider the following XUL file snippet that defines a text box component by id "nameTextBox".  Notice that there are no attribute specifying event handler calls; these are not needed when using data binding.

<textbox id="nameTextBox">

Here is a snippet of the corresponding controller onLoad method.  Notice how you create a binding between the model's name property and the nameTextBox XUL component value property.

public class FooController extends AbstractXulEventHandler {
	...
	onLoad() {
		bind(fooViewModel, "name", "nameTextbox", "value");
	}
	...
}

Behind the scenes this bind method is retrieving a XulComponent by id "nameTextBox" and linking the name property of the model to the value property of the XulComponent instance.  Now let's say in addition to binding the name property to a text box we want to wire the enabled state of an OK button to a boolean bean property on the model.  We would just add another bind call that ties the two properties together, notice the "!" expression which does an in-flight negation of boolean value (very helpful with XulComponents which use disabled rather than enabled properties).

onLoad() {
	bind(fooViewModel, "name", "nameTextbox", "value");
	bind(fooViewModel, "okEnabled", "okButton", "!disabled");
}

Event Handling and non-Component-state Events

In the diagram you will see that the XulDom fires "events" to the FooController.  As we mentioned before, these events are not state changes of a XulComponent, rather these are launching and routing operations such as displaying a dialog.  Events are declared in xul source file as tag attributes, e.g.:

<button id="searchDialog" label="Search" onclick="fooController.displaySearchDialog()" />

When PXF parses this tag, it will register an onclick property on the button component such that when the button is clicked PXF will try to find a registered event handler by the name of "fooController" and call it's displaySearchDialog method.   You register an event handler on a XulDomContainer object like so (you will likely do this during your application's initialization):

XulDomContainer container = xulLoader.loadXul("/path/to/my/xul/source/file.xul");
container.addEventHandler(fooController);

Foo Example Source

FooViewModel.java
// Manages the state of the view or form.  This includes component values as well as enablement,
// visibility and form validation.  Your ViewModel could delegate to or synchronize with a (persistent) domain
// model if you require.
public class FooViewModel extends ViewModel {
	private boolean okEnabled;
	private String name;

	public boolean isOkEnabled() {
		return okEnabled;
	}
	// Any bean property that is using for binding must fire property change events
	public void setOkEnabled(boolean okEnabled) {
	 	this.okEnabled = okEnabled;
		firePropertyChange("okEnabled", null, okEnabled);
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		firePropertyChange("name", null, name);
		validate();
	}

	// A very simple validator that allows the user to procede if there is anything in the name field.
	// Notice that we are not manipulating  UI component here to achieve this, just a bean property.
	private void validate() {
		setOkEnabled(!StringUtils.isEmpty(name));
	}
}


FooController.java
// The Controller has two roles 1) setup bindings for view state and 2) handle UI events
// A Controller should not contain references to XulComponent or subclasses.  If you find yourself
// referencing these, you should probably be binding to them instead.
public class FooController extends AbstractXulEventHandler {
	private FooViewModel fooViewModel;

	public FooController() {
		fooViewModel = new FooViewModel(); //FYI we prefer to inject the models via an IOC container, but for the sake of simplicity...
	}

	// This is the name with which to register instances of this controller.  Typically we would set this, but for simplicity we hardcode it.
	public String getName() {
		return "fooController";  //this name should match event handler aliases in the XUL source file
	}

	// Here we are binding two properties from the model to properties on two XUL components.
	public void onLoad() {
		bind(fooViewModel, "name", "nameTextbox", "value");
		bind(fooViewModel, "okEnabled", "okButton", "!disabled");
	}

	// The XUL framework will find and call this method when the Search button is clicked
	public void displaySearchDialog() {
		XulSearchDialog searchDialog = new XulSearchDialog();
		searchDialog.show(); //I wish launching a XulDialog were this easy!
		this.name = searchDialog.getName();
	}
}




Quick Reference

Model

  • represented as a Java Bean (with property change support)
  • "models" view state as bean properties
  • contains view logic such as enablement and visibility
  • applies form validation

View

  • represented in the XUL dom (via XUL source file)
  • contains syntax for creation of UI components
  • layout of UI components

Controller

  • represented as a Java class that implements XulEventHandler
  • binds UI state to the model
  • performs UI evnt handling (not related to component state) such as launching of dialogs (XulDialog and XulWindow)
  • should *never* reference XulComponent or subclasses
  • No labels