Generate Your UI Using Metadata +

The team is small but tasks are legion. Business changes and you have to keep up. And you are doing ajax. You have to keep up.

This is the situation we are in. And here is how we ease the pain: we are generating the user interface.

Generating a Form

To generate a form using our framework, you need to tell it :

  • what should be edited
  • the type of what is edited

Say we are building a form to edit a user object that contains user ip addresses:

var user = {
	name:"Sebastian Schuth",
	ipAddress:"192.168.1.33",
	active:true
}

The form fields needed are described using object literals:

var fields = [
	{
		key:'name', //name of the edited property
		label:'User name', //label to use in the form
		type: 'string',//type information
		permissioning:{denied: false, readonly: false}
	},
	{
		key:'active',
		label:'User is active',
		type: 'boolean',
		permissioning:{denied: false, readonly: false}
	},
	{
		key:'ipAddress',
		label:'IP Address',
		type: 'ipAddress',//this is a custom type
		permissioning:{denied: false,readonly: false}
	}
];

The properties needed for each field are:

  • key – name of the property that gets described by the entry
  • label – description shown as label inside the form
  • type – the type of the data stored in the property. This information is used by the framework to choose an editor implementation
  • permissioning – data about what the user is allowed to do with the entry. This reflects what the server allows the user to do.

Using this information, we can create a form using the following API:

/**
 *

Adds a form with into the given container element using the given fieldDefinitions.

 *

The structure of the form's contents depend on the editor implementation inside fieldDefinitions. This
 * function creates just a form element as container for the elements the editors return and which it adds to the DOM.

 *

A Save button is added to the form as long as the addSaveBtn
 * config option is not set to false.
 * The fnOkCallback function must takes one parameter: it gets a new object filled with the values from the
 * created form.
 * 

 *
 * @param {object} namespace                The javascript portlet namespace (used to get I18N information)
 * @param {Element} container               HTML node to which the form is added
 * @param {object[]} fieldDefinitions       The definitions of the form fields
 * @param {function} fnOkCallback           Function which is called when the form's save button is clicked
 *                                          (if no save button is added (oConfig.addSaveBtn === false),
 *                                          this function is never called!)
 * @param {object} [oConfig]                Optional configuration.
 * @config {object} [formAttributes]        Attribute definitions for the form as key/value object.
 * @config {boolean} [addSaveBtn]           If false, no save button gets added to the form. Defaults to true.
 * @config {object[]} [additionalButtons]   Array of additional buttons that should get added left of the save button
 *                                          (if a save button is added) to the form.
 *                                          The contained objects need to members:
 *
    *
  • label {string} Label of the button.
  • *
  • callback {function} Function called if the button gets clicked.
  • *
* Note: Make sure to stop the click event inside the callback function * to make sure that the form does not get send! * * @return {function} An error handling function that can be called to check for errors and add error messages to the * form as needed. */ agimatec.scaffolding.createForm = function(namespace, container, fieldDefinitions, fnOkCallback, oConfig){ // ... }

(as you can see, we are using JSdoc toolkit to generate our API docs)

So, let’s create a form:

agimatec.scaffolding.createForm(this, formContainer, fields, null);

This call does two things: it creates a form inside formContainer and adds editor objects (read on to learn more about them!) to the field description objects given to it.

Now, we need to fill the form:

agimatec.scaffolding.fillFields(user, fields);

Now, we can see what we have constructed:

Form generated by the example code

Form generated by the example code

Reading the values from the form is as easy as:

var objectFromForm = {};
agimatec.scaffolding.readValues(objectFromForm, fields, callbackWhenDone);

After this call, objectFromForm contains all data as edited by the user.

About Editors

Inside the framework, editor objects are used to create the form parts needed to edit object properties. All an editor does is create a fieldset which is added to the form created by agimatec.scaffolding.createForm(), which itself gets added to the DOM.
This means that if a special kind of data entry is needed, you will need to implement a special editor, which has to provide the following functions:

  • getReadWriteElement() – returns the DOM element used to edit entries
  • getReadElement() - returns the DOM element used to display
  • setValue() – this is called by the framework when using agimatec.scaffolding.fillFields()

We have put together some editors (current count: 21), including ones to connect objects to other objects, upload files, entering IP addresses, weekdays, dates, times and so on. This makes maintaining our webapp much easier and guarantees the consistency our customers appreciate.

Now entering the stage: Metadata

Well, as easy defining fields using object literals is, this does not solve the issue of additions and changes to existing objects. This is where we added metadata to the mix. The metadata we get from the server tells us enough to create the field definitions we need to generate the forms!

So here we go:

	/**
	 * Gets the field definitions for a dynamic form element (@see scaffolding.js).
	 * This function returns the sorted field definitions for a specified object. These describe which
	 * editor is used and adds further information like key and label to it.
	 * If we have an enum type, it adds also the options to an feature object.
	 * Features:
	 *
    *
  • sorting of field definitions using a bean's SORTING feature
  • *
  • AJF type matching for root type and all related beans (depends if the feature "include" is set)
  • *
  • adding type of modelObject AJF type as feature "refBeanId"
  • *
* * @param {object} namespace The javascript portlet namespace (used to get I18N information) * @param {string} beanInfoId The bean info id (e.g. 'com.agimatec.connecta.model.XFireTemplate') * * @return {object[]} Field definitions for the given beanInfoId. */ agimatec.metadata.getFieldDefinitions = function(namespace, beanInfoId) {/*...*/};

All you need to pass is which kind of object you want to display, the rest is handled by the framework.

Whats next

In this post, i explained how our framework makes creating forms for data entry easy and how we use metadata to make things even more “magic”. But this is not the end of the story. Our metadata is generated in a multi-step process, which will be described in a future post. To wet you appetite: we are generating metadata from multiple sources: Java Beans and XML descriptors and are able to merge multiple descriptors and user-based permissions.

Of course, generated UIs always need to be highly configurable to meet your user’s needs. Our framework offers a lot of options for this, up to defining rules that can manipulate the form reflecting what the user entered (and it looks like: formManager.when("lifecycleStatus").isSetTo("ACTIVE").changeForm(/*...actions...*/)). This makes the forms more friendly to use and prevents the user from doing things that make no sense. If you are interested, i would like to hear from you, just leave a comment.

Another theme i did not mention in the example is client-side validation. This is built in, too. And it is not hard to use, but explaining it would have made this article even longer. Again: if you are interested, leave a comment. If there is interest, i will blog about it.


This is the fourth article in my mini-series about developing highly ajaxified portlets. In this blog, you can also find articles about:

6 Responses to “Generate Your UI Using Metadata”

  1. [...] agimatec » Blog Archive » Generate Your UI Using Metadata [...]

  2. I am always amazed that everybody is still building the actual UI in some form of code. It would be better if the business people can lay that stuff out and if it is for example driven through a CMS instead thrrough code or configuration in code. I blogged about this at hrere.

  3. [...] Generate a GUI using meta data, because most of the portlets are created by scaffolding from meta data. [...]

  4. [...] Metadata for scaffoling your GUI (described here). [...]

  5. Hi,

    very interesting post, and describes exactly what we try to do as well. I wondering why META Data driven UIs are not more common this days. Anyway, you describe what to do exactly but there is no reference to the tools you use. are this the same as you have described int the newer post http://www.agimatec.de/blog/2009/04/influencing-ajax-guis-with-metadata ?

    Thanks a lot for any advise, we are looking to build this approach for a non Java world (for embedded devices)

    Thanks a lot,

    Holger

  6. Hi Holger!

    Yes, the second post (written by Roman) describes how we generate the metadata for the objects coming from the server.

    We use a JSON-like format as description of the objects, as you can see in the examples of the second post. One cornerstone of our solution is the ability to merge metadata definitions from multiple sources (generated using knowledge about the model objects or user rights or hand-crafted).

    Another key part of the way we build our UIs are the editors used to display, create and change properties. The editors are where we have put a lot of work into and they are getting complex if you try to get fancy stuff going. But on the other side, they are relatively easy to test isolated from the rest of the system.

    All tools used by us to generate metadata driven UIs are self-written and not available to the public (at least by now – you never know…).

    If you have further questions, don’t hesitate to ask – i am always happy if i can help.

    Sebastian

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>