Baobab

"JSON → SWT shells" GUI Transformer tool for Java developers

View the Project on GitHub milanaleksic/Baobab

Wish to see just the basics?

Documentation

Overview

You use Baobab to convert JSON to create SWT beans using:

  1. explicit class mention,
  2. shortcut notation or
  3. builder notation.

and then to configure them you use property setters. As usual, make sure the property you are setting value to is exposed in the means of setter method or public field.

Baobab uses type information to find out which converter to use to map the JSON node value to the target object.

If you wish to add children widgets to the current widget, or to know how to name the widget, or to define types you need to know everything about the special tags.

If you wish to use resource bundle messages, images or beans injections into SWT (or your custom) beans you should definitely take a look at how to implement providers.

After you have finished working with the user interface, you need to be able to use all those widgets and to react on their events, right? Take a look how I implemented events and widgets annotation-driven embedding.

Finally, starting from version 0.3.0 Java controller part can be made simple even further if you choose to utilize the Observed model approach which eliminates need for embedded components in most cases (still, you can continue to use embedded components in parallel if you wish to).

When the moment comes to combine all elements and run the application you need how to execute call to transformer and wiring it all together.

Creation notations

Explicit class mention

To create beans using this approach, you should use the special tag _type to note exact class you wish to instantiate:

    {
        "_type" : "org.eclipse.swt.widgets.Label",
        .....
    }

Shortcut notation

This approach is used to make definition writing faster since it allows you to use short type identifiers instead of full class names.

    {
        "_type" : "label",
        .....
    }

If you wish to see entire list of supported shortcuts out-of-the-box in Baobab, take a look at the current version's knownShortcuts field in config file.

Wish to extend?

If you wish to extend the list of the items to use some additional SWT classes or if you wish to use shortcuts for your own widgets, you can create properties file <root>application.conf like I did it in MovieCatalogSystem. If you use this approach and wish to use Baobab's interactive editor for your project's GUI files, just include the compiled output to the editor's classpath.

Builder notation

For some complex cases it is too exhausting to set all properties manually, especially if the widget appears too often. Builder pattern should help you with this problem by lowering the amount of text you need to enter to get the same result.

Notation is as follows:

    [builderName](param1,param2...)

The param count is generic. Implementation of net.milanaleksic.baobab.builders.Builder<T> has only one method create that gets List of all parameters.

You can use builder notation in 2 different ways:

  1. as a String value,
  2. as the value for _type in the JSON object notation.

First approach is trivial - it lets builder do entire task for creating the object:

    "someProperty" : "[myBuilder](param1,param2)"

But, in case you need to further customize the object created by the builder, use second approach:

    "someProperty" : {
        "_type": "[myBuilder](param1,param2)",
        "param3" : "valueForParam3"
    }

Example: GridDataBuilder

Builder notation is used in Baobab to replace creation of the org.eclipse.swt.layout.GridData object which can be quite cumbersome if you base you layout on it a lot. Using GridDataBuilder you can replace following block of code:

   "_type" : "composite",
    "layoutData" : {
        "_type" : "gridData",
        "horizontalAlignment" : "{center}", // what are these {???} things? Wait for Integer Converter part
        "verticalAlignment" : "{beginning}",
        "grabExcessHorizontalSpace" : true,
        "grabExcessVerticalSpace" : false
    },

with following shorter variant:

    "_type" : "composite",
    "layoutData" : "[gridData](center,begin,true,false)"

This builder has variants of 4 and 6 parameters, where the 6-param version also accepts horizontalSpan and verticalSpan properties to be set. But what if you have to set some additional field for GridData, like heightHint? Easy, use the object + builder notation approach:

    "layoutData" : {
        "_type" : "[gridData](fill,fill,true,true,1,3)",
        "heightHint" : 180
    },

Wish to extend?

First, you need to make your own implementations of interface net.milanaleksic.baobab.builders.Builder<T>.

You need to modify your Typesafe Config file /application.conf to include following form of config pairs inside package net.milanaleksic.baobab.builders:

  myBuilderName=com.foo.MyBuilder
  myBuilderName2=com.foo.MyBuilder2
  ...

The previous mapping will allow you to register (e.g.) a builder with name myBuilderName which is defined in the class com.foo.MyBuilder. This builder will receive all parameters sent to it in the GUI file. The response must be instance of net.milanaleksic.baobab.builders.BuilderContext<T> which describes the built SWT component and its name. If you choose to return null, it will not be placed in the map on named components.

As you can see, extensibility of the builder notation allows you to create you own tiny DSL inside the GUI files for easier creation of GUI interface files.

Converters

Converters are critical part of any Baobab-driven application. JSON only recognizes Strings, booleans and number values. How can you possibly map a TabItem? The trick is in converters - they are specialized for converting a JSON node (which can be text, number, boolean, array or object node) into a specific target type.

To detect target type, Baobab uses type introspection at runtime. For example, when it encounters a field of type int, it will use IntegerConverter to convert the value on the right side of JSON property to appropriate type - int. IntegerConverter is, though, more powerful because it has magic value notation, which you can read in details later.

Currently supported converters (most of them are in package net.milanaleksic.baobab.converters.typed) are:

  1. BooleanConverter
  2. ColorConverter
  3. IntegerConverter
  4. IntegerArrayConverter
  5. FontConverter
  6. ImageConverter
  7. PointConverter
  8. StringConverter
  9. if none is appropriate - fallback to net.milanaleksic.baobab.converters.ObjectConverter

Wish to extend?

You need to modify your Typesafe Config file /application.conf to include following form of config pairs inside package net.milanaleksic.baobab.converters:

  com.foo.TargetType=com.foo.MyConverter
  com.foo.TargetType2=com.foo.MyConverter2
  ...

The previous mapping will allow conversion of a JsonNode (Jackson is used for JSON processing) to the type (e.g.) com.foo.TargetType using an implementation of interface net.milanaleksic.baobab.converters.Converter or maybe (easier/cleaner for simple type mapping) net.milanaleksic.baobab.converters.typed.TypedConverter defined with the right side of the property - com.foo.MyConverter and so on.

BooleanConverter

This converter is the most basic one - it just converts the JSON native boolean value to Java's. Nothing else to it.

ColorConverter

This converter can be used in 2 different ways:

  1. it allows you to use classic CSS color notation in GUI files (which is in my opinion cool),
  2. you can reference SWT system colors by using the name of the color in lowercase.
    {
        "_type" : "label",
        "foreground" : "#0000ff",              // HTML/CSS color definition: r:0, g:0, b:255
        "background" : "color_list_foreground" // system color org.eclipse.swt.SWT.COLOR_LIST_FOREGROUND
    }

IntegerConverter

Allows not only exact integer value to be placed as property value, but also to use magic number notation to reference the colors from the SWT class (but in lower case). This approach can be combined with the special tag _style used to set the widget's style when creating instance.

Since it is quite often needed to use bitwise OR when declaring values (styles before all else), IntegerConverter also accepts OR operator:

    {
        "_type" : "text",
        "_style" : "{multi}|{wrap}|{v_scroll}|{border}" // SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.BORDER
    }

IntegerArrayConverter

The reason why this converter was made was the SashForm's weight property which demands array of integers.

    {
        "__comment" : "to set sashForm's weights you need to wait until components have been added",
        "_type" : "sashForm",
        "_style" : "{smooth}",
        "_children" : [
            {
                "type" : "composite"
                // ...
            },
            {
                "type" : "composite"
                // ...
            }
        ],
        "weights" : [ 2, 1 ]
    }

Since it uses (under the covers) the IntegerConverter, you can send magic constants, not only explicit numerical values.

FontConverter

This converter expects an object notation on the right side of the property with one of following properties:

  1. name - font name face,
  2. height - height of the font
  3. style - it can be set to text values bold or italic

For whatever is not set, FontConverter will use values from default system font.

    {
        "_type" : "label",
        "font" : {
            "height" : 12,
            "style" : "bold",
            "name" : "Courier New"
        }
    }

ImageConverter

This converter just delegates the image name to image provider to do whatever it wants with it.

For example, in MCS I have image provider implementation that uses the value as a full path to the location in application resources, from where it fetches it for Baobab to embed.

    {
        "_type" : "toolItem",
        "image" : "/net/milanaleksic/mcs/application/res/media.png"
    }

PointConverter

Very basic converter that uses simple string pattern x,y as the value definition.

    {
        "_type" : "shell",
        "size" : "412,326"
    }

StringConverter

What ever string you enter on the right side of the property, it will be set in the bean's property.

If you put part of the text in the square brackets ([]), messageProvider will be asked for the value behind it. That should be, most logically, a String from the resourceBundle for the current application locale, but doesn't have to be.

    {
        "_type" : "label",
        "text" : "[delete.doYouReallyWishToDeleteMovie]"
    }

ObjectConverter

Object converter is the fallback converter. This means that when Baobab finds a field which type is not directly registered in the list of converters, it goes to ObjectConverter and says "Help!".

Providing objects using injection and providers

First thing ObjectConverter tries to do is to check if the object is maybe in injected object notation, which means that instead of creating it it needs to provide it. The injected object notation is:

    "(objectName)"

Providing the object is two-phase process: first the named objects in the current context are browsed. You can use the special tag _name to give name to any bean. This is extremely useful when working on containers like TabFolder, where you first need to create container contents widget (Composite for example) and then create TabItem with the previously created widget as the control:

    {
        "_type" : "composite",
        "_name" : "settingsTab"
    },
    {
        "_type" : "tabItem",
        "text" : "[settings.basicSettings]",
        "control" : "(settingsTab)"
    }

If none named bean is found ObjectProvider is asked for the object under the name used. If you implemented Object Provider to fetch the object from DI using the name, you can easily inject a bean in custom SWT widgets.

    {
        "_type" : "myCustomComponent",
        "bundle" : "(resourceBundle)" // resourceBundle can be Spring bean for example!
    }
Object creation

If the injected object notation is not used, ObjectConverter tries to render the value of the property setter as JSON object and finds in it the special tag _type to see which type of object it needs to instantiate before proceeding with property setting (unless short children notation is used). If you jumped over the first topic of the documentation - creation notations, now is a good time to read it.

Special tags

Special tags are used for situations when JSON limitations become apparent. In practice, special tags are only JSON node names which are not based on the property name in the target widget / bean.

_type tag

This tag is used in object nodes to say which class/builder/shortcut to use to create the object. More details in the creation notations part.

_children tag

What is quite specific for SWT widgets, controls etc. is that no-arg constructor does not exist. If you wish to create a new widget, you have to explicitly say what is the parent and what is the style (or style mix) you wish to enforce.

So, this tag was introduced to allow creation of "children" components by injecting the parent component while constructing the child. For example, if you wish to make shell with two labels you do it like this:

    {
        "_type" : "shell",
        "size" : "150,150",
        "layout" : {
            "_type" : "org.eclipse.swt.layout.RowLayout"
        },
        "_children" : [
            {
                "_type" : "label",
                "text" : "first label"
            },
            {
                "_type" : "label",
                "text" : "second label"
            }
        ]
    }
Short children notation

To ease the pain of too much text, there is a short children syntax which replaces array with JSON object notation. This shorter notation improves readability and lowers the GUI definition file size by 15-25% in number of lines. As a side effect definition file is by far more fluent to "follow the GUI tree". In practice, short children syntax collects information from _style, _name and _type special tags and sets them all at once in the JSON key name. Following two examples are identical in what they are producing:

    {                                                               {
        "_type" : "shell",                                              "_type" : "shell",
        "layout" : {                                                    "layout" : {
            "_type" : "gridLayout",                                         "_type" : "gridLayout",
            "numColumns" : 1                                                "numColumns" : 1
        },                                                              },
        "_children" : [                                                 "_children" : {
            {                                                               "text(textName, {border}|{v_scroll})" : {
                "_type" : "text",                                               "layoutData" : {
                "_style" : "{border}|{v_scroll}",                                   "_type" : "[gridData](fill,fill,true,true)"
                "_name" : "textName",                                           }
                "layoutData" : {                                            },
                    "_type" : "[gridData](fill,fill,true,true)"             "org.eclipse.swt.widgets.Label(labelName)" : {
                }                                                               "text" : "hello world!"
            },                                                              }
            {                                                           }
                "_type" : "org.eclipse.swt.widgets.Label",          }
                "_name" : "labelName",
                "text" : "hello world!"
            }
        ]
    }

As you can see, the syntax is type_syntax(object_name[,style_syntax]), which means that style parameter is not mandatory but name is. At the same time, any (valid) type node value is allowed to be placed in the type_syntax part (explicit class mention, shortcut notation and builder notation).

One bad thing with this approach is that you have to always give names to the components, otherwise (because of the nature of JSON), it will overwrite all previous nodes with the last one in case there is more than one node with the identical json key value (same combination type_syntax+object_name+style syntax) and thus create only one item. The good news is that you can use _* syntax which tells the Baobab not to push the widget name to transformation context (and thus not pollute it). If you wish to use this approach you need to have unique names only in once children collection (they don't have to be unique throughout entire definition file).

From this point on examples will use short children syntax so you can get used to it (I believe it is more human-friendly than the standard _type syntax).

_style tag

Since style is set during the creation of the SWT widgets, normal setter does not exist. That was the reason for inclusion of this tag. Since style is of integer type, you are using IntegerConverter when setting the value, which gives you the freedom of using SWT's magic constants.

_name tag

Name tag has a single usage - putting the widget in the TransformationContext. This context object is the one you get after calling transformation on the GUI file from the Transformer (Baobab entry point).

All possible names are allowed, as long as they don't take trailing spaces and comma. But, if you decide to name your component with a leading "low line" symbol (e.g. _label1 or _comp etc.) the widget will not be put in the output transformation context. This is done on purpose to allow "short children syntax".

After you have named some widget, you will be able to reference that object inside:

  1. the same GUI file (you have the example for this in ObjectConverter part)
  2. you can attach event listeners in the Java code to it using the name of the reference using @EmbeddedEventListener/@EmbeddedEventListeners annotations,
  3. you can embed (inject) the widget reference in the code using @EmbeddedComponent,
  4. you can dereference named widgets from the transformationContext using call net.milanaleksic.baobab.TransformationContext.getMappedObject(String name).

Examples for the reference and event embedding are given in the embedding part.

__comment tag

This is trivial special tag to be used if you have need to comment something in the GUI code. Proper JSON should not have JavaScript-styled comments so the only way to have a proper JSON is to use a custom JSON field which will be omitted by content parsers. So, just put it in any object definition and put in the value field whatever you wish - it will not be taken into consideration by Baobab.

    "label(myLabel)" : {
        "__comment" : "This is a comment in the GUI file!"
    }

Providers

If I believe there is an extension point that you will really really like to use, there is a provider interface for it. You use your dependency injection framework to say "here is the implementor for the provider", and like magic you'll see our application messages, images from your resources or external location or even other beans from your DI framework showing up as possible candidates for wiring into Baobab definition JSONs.

Messages string provider

Use your DI container to override net.milanaleksic.baobab.providers.ResourceBundleProvider with your implementation or just route it to SimpleResourceBundleProvider to always use messages_en.properties (or default messages.properties if first does not exist) as the source for resource naming pattern for string properties. For example:

    "label(myLabel)" : {
        "text" : "[string.from.bundle]"
    }

Object provider

When you use named object syntax to ask from the DI for a named object in JSON definition, you basically ask it from implementation of net.milanaleksic.baobab.providers.ObjectProvider you registered in the DI container. You can though use AlwaysReturnNullObjectProvider to always embed null when named object is requested, for example:

    "net.milanaleksic.application.MyCustomSWTComponent(coolName)" : {
        "someReallyCoolCustomObjectFromSpring" : "(customSpringObject)"
    }

Images provider

Use your DI container to override net.milanaleksic.baobab.providers.ImageProvider with your implementation or just route it to AlwaysReturnEmptyImageProvider to always embed dummy image with the value of the JSON value node for the image setter.

Embedding

Embedding is the process of wiring back all the components you created in the interface GUI files to the Java code. To be able to reference widgets (or other beans) you created in the GUI file they have to be named using special tag _name.

Java files that have identical name and package position as the GUI file are called managed forms since you can embed components and event listeners directly using the net.milanaleksic.baobab.Transformer.fillManagedForm(Shell parent, Object managedFormObject). When Transformer analyzes the managedFormObject it will find all the fields with embedding annotations and either inject values into them or attach listeners, depending on the annotation. It is important to remember that Baobab expects the GUI file to be named exactly like the Java managedFormObject from the call, just having .gui instead of java/class.

If you give your widget a name then you can include a field of type org.eclipse.swt.widgets.Listener and annotate it with net.milanaleksic.baobab.EmbeddedEventListener. This annotation specializes the binding to make sure proper named component and event is listened to via this field listener. There is even a shorter way, called "method level listeners", where you put the annotation not on the field of type Listener but on a method, which removes a lot of clutter.

@EmbeddedComponent

This annotation can be used on fields (should be but don't have to be private) in the form file.

If you wish to reuse a event listener method or field for diferent events, you can use net.milanaleksic.baobab.EmbeddedEventListeners to group more than one annotation on a single listener.

So, if you have a field named "myLabel" in the GUI file:

     "label(myLabel)" : { }

in the managed form Java file you just mark the appropriate field with the annotation:

    @EmbeddedComponent
    private Label myLabel;

After transformation, you can just dereference the value in it - it will be injected by Baobab before that time or it will fail early while trying to do so.

@EmbeddedEventListener and @EmbeddedEventListeners

To wire event listeners you also need just to reference the component with the name and then ask for a specific event to attach the listener to. Listener implementation fields can be but don't have to be static.

    @EmbeddedEventListener(component="myLabel", event= SWT.Selection)
    private final Listener myLabelSelectionListener  = new Listener() {
        public void handleEvent(Event event) {
            // handle event
        }
    }

Latter annotation (EmbeddedEventListeners) is just a helper annotation in cases where you want to use same listener implementation for multiple event sources for one or more event sources:

     @EmbeddedEventListeners({
            @EmbeddedEventListener(component = "comboMediumType", event = SWT.Selection),
            @EmbeddedEventListener(component = "comboMediumType", event = SWT.Modify),
            @EmbeddedEventListener(component = "comboGenre", event = SWT.Selection)
    })
    private final Listener complexListener  = new Listener() {
        public void handleEvent(Event event) {
            // handle event
        }
    }

Method-level listeners

In case you think the creation of event listener objects is cumbersome - you are right. That is why starting from version 0.1.7 method-level listeners are allowed.

Candidates for method level listeners must fulfill following requirements:

  1. They must be marked with @EmbeddedEventListener or @EmbeddedEventListeners,
  2. they must have void return type,
  3. they can have either one argument of type org.eclipse.swt.widgets.Event, or none (the latter when you just don't need the event details).

Here is the method-level implementation of previous two examples:

    @EmbeddedEventListener(component="myLabel", event= SWT.Selection)
    private void myLabelSelectionListener(Event event) {
        // handle event
    }

    @EmbeddedEventListeners({
            @EmbeddedEventListener(component = "comboMediumType", event = SWT.Selection),
            @EmbeddedEventListener(component = "comboMediumType", event = SWT.Modify),
            @EmbeddedEventListener(component = "comboGenre", event = SWT.Selection)
    })
    private void complexListener(Event event) {
        // handle event
    }
Handling runtime exceptions in method-level listeners

If a runtime exception is thrown in code executed by your listeners (SWTException is one of them), your main SWT dispatch loop will either be prepared for it or face the consequences - immediate program shutdown. How to deal with this issue and still avoid using try/catch in all of your listener methods? By registering net.milanaleksic.baobab.converters.MethodEventListenerExceptionHandler in the Transformer configuration.

If you do so, the exception will be caught in Transformer and delegated to this exception handler so your app does not face the problem in runtime. You are free to use appropriate mechanisms to handle the exception situation (message box / log / send email...). The fact to remember is: if you do not register a handler for exceptions Transformer will rethrow the exception and probably cause undesirable effects for your application UX.

Wiring it all up

Transformer should already have all of its dependencies wired before calling it using your dependency injection. Guice and Spring are the frameworks I used:

  1. if you use Spring, take a look at XML wiring used in the MovieCatalogSystem,
  2. if you use Guice, there is already a basic module defined in the Baobab code.

The code for wiring the form and GUI file uses the entry point to Baobab - Transformer.

Calling the transformer is quite straight-forward:

  @Inject private Transformer transformer;

  public showForm() throws TransformerException {
    net.milanaleksic.baobab.TransformationContext context = transformer.fillManagedForm(this);
    org.eclipse.swt.widgets.Shell shell = context.getRoot();
    shell.open();
  }

Now, what the call to will do is the following:

  1. it will search for a GUI file named identically to the name of the class (and in the same package), just with extension .gui;
  2. it will check the validity (JSON) of the GUI file, wire all of the injected named objects, inject messages from resource bundle;
  3. it will boot strap all of @EmbeddedComponents and @TransformerModel fields (more in observed model part for @TransformerModel) ;
  4. wire all event handlers fields/methods marked with @EmbeddedEventListener annotations;
  5. it will return a TransformationContext object which wraps the transformed shell and all of the mapped objects and model binding metadata which is critical if you wish afterwards to manually update SWT form based on current model state using call transformer.updateFormFromModel(form, context).

Accessing widgets

You can access all the components in the UI either

  1. by asking for them using their name (something similar to the Servlet getAttribute/getParameter approach) using net.milanaleksic.baobab.TransformationContext.<*widgetClass*>getMappedObject(*widgetName*)

  2. or using annotation net.milanaleksic.baobab.EmbeddedComponent in your form class to have the component injected for you (approach based on dependency injection pattern)

In my opinion, first approach is useful for the cases when you wish to execute a simple and small action, like setting a dynamic text in some component (e.g. web service version fetched from far away). Second approach is useful when you wish to maintain reference to the widget throughout the life of the form (e.g. you need to keep reference to username/password text widgets until the user clicks on OK).

Observed model approach

Starting from Baobab v0.3.0 and with help of Cglib magic it is possible to simplify further the usage of Baobab using embedded model which wraps the binding between a standard Java object expressing the current state and the view presented to the user in the shape of SWT form. I believe the best way to show how to use it is via simple example.

Let us imagine a situation where a form called MovieDetailsForm uses Baobab for mapping the SWT form to a simple model object. This MovieDetailsForm can look something like this:

public class MovieDetailsForm {

    @EmbeddedComponent
    private Shell shell;

    @TransformerModel(observe = true)
    private MovieDetailsModel model;

    public void showDataForMovie(Shell parent, Movie movie) {
      model.fillFromMovie(movie);
      shell.setVisible(true);
    }
}

There should not be any surprises in the form controller object described above besides the @TransformerModel. Let's see how this model looks like.

First of all, every model is a POJO which uses only annotations for customizations (if needed at all) and no inheritance or interface implementation. Here is a little more elaborate model to show different aspects:

package net.milanaleksic.mcs.application.gui.model;

import com.google.common.collect.Sets;
import net.milanaleksic.baobab.model.*;
import net.milanaleksic.mcs.domain.model.*;

import java.util.Set;

public class MovieDetailsModel {

    private String comment;

    @TransformerProperty("selectedItems")
    private Iterable<String> tags;

    @TransformerProperty(component = "movieName", value = "text")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Iterable<String> getTags() {
        return tags;
    }

    public void setTags(Iterable<String> tags) {
        this.tags = tags;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    @TransformerFireUpdate
    public void fillFromMovie(Movie theMovie) {
        final Set<String> tagNames = Sets.newLinkedHashSet();
        for (Tag tag : theMovie.getTags()) {
            tagNames.add(tag.toString());
        }
        tags = tagNames;
        comment = theMovie.getComment();
        name = theMovie.getTitle();
    }

}

So, what model wrapping does is following: it goes through all of the fields which don't have the annotation @net.milanaleksic.baobab.model.TransformerIgnoredProperty. It finds the named components for all of these fields and maps the text property of them to the value of the property in the model. This means that modifying the value in the model and then asking the Baobab toolkit to update form based on model will force the value of the SWT component to be updated to this value. At the same time Baobab will update the model field when SWT component's text value gets modified using dynamically created event listener for SWT.Modify event.

Both default choices SWT.Modify trigger and the bound property text can be modified using customization annotation @TransformerProperty.

The hardest part with working with object is to understand that there are different ways to update form when after updating the model:

  1. If you have set (back in the form controller object) @TransformerModel's attribute observe to true, Baobab will create Cglib-driven listeners for all setters of property-bound fields. Property-bound fields are all the fields in model that don't have @TransformerIgnoredProperty. This listener will execute immediately after the setter finishes with execution;
  2. If you have set (back in the form controller object) @TransformerModel's attribute observe to true, Baobab allows you to update more than one field in a single method to maintain UX consistency using @net.milanaleksic.baobab.model.TransformerFireUpdate. If Baobab finds a method in the model object annotated with this annotation it will create additional Cglib-driven listener which will execute the form update after this method has finished executing;
  3. Third (and last) method of updating the component does not demand you to have the observe attribute set to true - you can execute it whenever from the form controller. It is based on the Transformer's call transformer.updateFormFromModel(form, context). Please note that to be able to use this call you need to maintain the model binding metadata by yourself (you receive it as part the context object which is result of the Baobab transformation call).