Errai: The browser as a platform

Tuesday, September 18, 2012

Errai 2.1 is On The Way

Throughout our summertime communications blackout (sorry about that), the Errai team has been working overtime to further polish and stabilize all the great features in Errai 2.0 while also introducing a bunch of brand new features. This article outlines all the major changes between Errai 2.0 and Errai 2.1.

If you are new to Errai, the quickest way to get started is via the archetypes described in our Getting Started guide.


New in 2.1

We're calling these new features in 2.1 "preview" because we want a chance to incorporate your feedback before we lock down the APIs in 3.0. We crave your input: share your use cases, feature requests, and of course pull requests.


Client-Side JPA


Errai 2.1 adds JPA2 to our Java-EE-6-in-the-client lineup. Errai JPA implements a subset of JPA 2.0, allowing familiar JPA code to do double-duty on the client and the server. For example, the following familiar-looking JPA entity can be persisted the in the browser's local storage:


@NamedQueries ({
  @NamedQuery(name="allAlbums",
              query="SELECT a FROM Album a ORDER BY a.releaseDate, a.artist"),
  @NamedQuery(name="albumByName",
              query="SELECT a FROM Album a WHERE a.name LIKE :name")
})
@Entity
public class Album {

  @GeneratedValue
  @Id
  private Long id;

  private String name;

  @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
  private Artist artist;

  private Date releaseDate;

  private Format format;

  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }

  ...

  @Override
  public String toString() {
    return "Album [id=" + id + ", name=" + name
            + ", artist=" + (artist == null ? "null" : artist.getName())
            + ", format=" + format
            + ", releaseDate=" + releaseDate + "]";
  }
}

Of course, this code does the same thing on the client as it would on the server:

@Inject EntityManager em;

public void createExampleAlbum() {
  Artist samNDave = new Artist();
  samNDave.setName("Sam & Dave");
  samNDave.addGenre(new Genre("Rock"));
  samNDave.addGenre(new Genre("Soul"));
  samNDave.addGenre(new Genre("R&B"));

  Album album = new Album();
  album.setArtist(samNDave);
  album.setFormat(Format.LP);
  album.setName("Hold On, I'm Comin'");
  album.setReleaseDate(new Date(-121114800000L));
  em.persist(album);
  em.flush();
}

public List<Album> fetchAlbums(String nameContains) {
  TypedQuery<Album> query = em.createNamedQuery("albumByName", Album.class);
  query.setParameter("name", "%" + nameContains + "%");
  return query.getResultList();
}

Get your hands dirty by playing with the demo, which you can find on GitHub.


ErraiUI Templates


As we clarified previously, GWT offers a wide array of choices when it comes to implementing your user interface. You can opt for declarative XML-based layout with UIBinder, programmatic layout with GWT panels and widgets, or even mash up GWT with something else like jQuery.

In Errai 2.1, we're adding a new option to the mix: Errai UI. Errai UI gives you declarative layout through templates that are valid HTML 5 documents, paired with Java classes that imbue behaviour into them. Of course, you get compile-time checks to make sure everything lines up properly. We want to help you avoid unpleasant surprises at runtime!

Here's an Errai UI template:


<!DOCTYPE html>
<div>
  <label for=itemName>Name:</label>
  <input id=itemName type=text data-field=name>
  <br>
  <label for=itemDept>Department:</label>
  <input id=itemDept type=text data-field=department>
  <br>
  <label for=itemComments>Notes:</label>
  <input id=itemComments type=text data-field=comment>
  <br>
  <button class=btn data-field=saveButton>Save</button>
</div>

The only special bits are the data-field attributes. These line up with field names in the companion Java class, like this:

@Templated
public class ItemForm extends Composite {

  @Inject @DataField private TextBox name;
  @Inject @DataField private TextBox comment;
  @Inject @DataField private TextBox department;
  @Inject @DataField private Button saveButton;

  public void grabKeyboardFocus() {
    name.setFocus(true);
  }

  @EventHandler("saveButton")
  public void onSaveButtonClicked(ClickEvent event) {
    // this method will be called for each click event on saveButton
  }
}

Each @DataField field is matched up with the corresponding element in the template, and a reference to it is injected. If you prefer, you can also declare fields as raw DOM element types rather than GWT widgets (for example, the name field could have been an InputElement rather than a TextBox). And you're not limited to just the standard DOM elements plus the widgets that come with GWT. You can inject your own widget types, even (especially?) other ErraiUI @Templated ones.

To handle events, simply annotate a method with @EventHandler("DataFieldName"). The method should take an argument of the event type you want to receive. Errai UI will ensure the source node sinks the appropriate events, and it will deliver them to your method each time they happen.

To learn more, check out the ErraiUI demo, and also read through Errai UI's reference guide.

But wait! There's more!

There are two more new Errai modules that go great with Errai UI: data binding and navigation.


Model-View Data Binding


Errai Data Binding is a great complement to Errai UI, but it's also just as handy in conjunction with other approaches to UI that you may already be using in your GWT and Errai apps.

Errai Data Binding lets you bind property values in your model objects to widgets in your UI.

To make a model object amenable to having its properties bound, annotate it with @Bindable:

@Bindable
public class Employee {

  private int id;
  private String name;
  private Integer age;
  private boolean active;

  public int getId() { return id; }
  public void setId(int id) { this.id = id; }

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

  public Integer getAge() { return age; }
  public void setAge(Integer age) { this.age = age; }

  public boolean isActive() { return active; }
  public void setActive(boolean active) { this.active = active; }
}

Then to establish data bindings to UI widgets, use the fluent configuration API:

public class EmployeeForm {
  private final TextBox nameTextBox = new TextBox();
  private final TextBox ageTextBox = new TextBox();
  private final CheckBox activeCheckBox = new CheckBox();

  private Employee employee;

  @PostConstruct
  public void init() {
    employee = DataBinder.forType(Employee.class)
      .bind(nameTextBox, "name")
      .bind(ageTextBox, "age")
      .bind(activeCheckBox, "active")
      .getModel();
  }
}

The model instance that comes back from the getModel() call is now automatically kept in sync with the bound UI fields.

Declarative Data Binding

When Errai Data Binding is used together with Errai UI templates, you can skip the fluent API and simply declare "auto-bindings." Going back to the ItemForm example:

  ...
  @Inject @AutoBound private DataBinder<Item> itemBinder;

  @Inject @Bound @DataField private TextBox name;
  @Inject @Bound @DataField private TextBox comment;
  ...

By injecting an @AutoBound DataBinder and adding the @Bound annotations on the widgets, we get a declarative approach to data binding. With the above code snippet in place, it will always be true that itemBinder.getModel().getName().equals(name.getText()).

Errai data binding also allows custom converters, wrapping existing model objects, and more. See the reference guide for all the juicy details!


"Multi-Page" Navigation


The third new UI-related feature in 2.1 is Errai Navigation. Errai Navigation adds a decentralized, declarative configuration mechanism to GWT's built-in History facility. This allows back-button and forward-button navigation, plus bookmarkable locations within your app. And since the configuration is declarative, it even produces navigation flow graphs like this at compile time:


The navigation system simply manages the contents of a panel (a div element in the DOM): based on what comes after the # in the location bar, the contents of that div change. Typically, in your app's entry point, you would add that panel to a large, central region of the document. Headers, footers, and sidebars can remain outside of this div, which allows them to stay in place when the user navigates between pages.

As an example, if you want the URL http://example/my-app/host-page.html#FunPage to cause the page body to contain an instance of your FunPage widget, you would do the following:

@Page
public class FunPage extends Widget {
  // whatever Widgety things you want to do
}

Pages are CDI beans, and the navigation obtains instances from the client-side bean manager when needed. So the above implicit-scoped bean would be newly instantiated each time the browser's URL bar changes to http://example/my-app/host-page.html#FunPage. If you'd prefer one instance of FunPage to hang around for the life of the app (simply appearing and disappearing based on the current URL fragment,) just annotate it with @ApplicationScoped.

But what good are pages without links between them? To make links between pages, inject TransitionTo instances like so:

@Page
public class FunPage extends Widget {

  @Inject TransitionTo<UltraFunPage> upgradeLink;
  @Inject TransitionTo<WelcomePage> quitLink;
  @Inject TransitionTo<SillyPage> sillinessEnsues;

  public void onQuitButtonClick(ClickEvent e) {
    quitLink.go();
  }

  // whatever Widgety things you want to do
}

To follow a link, simply call go() on the link in question.

These links make up the arrows in the navigation graph that's produced at compile time. The type parameter controls the widget (CDI bean) type that provides the page contents, and the field names are the label text on the arrows.

Of course it goes without saying that Errai Navigation goes great with Errai UI templates: a @Templated widget can also be a @Page.


Improved Since 2.0


That was a lot of new API, but we didn't stop there. We've also made a bunch of incremental improvements to the existing APIs from 2.0 and earlier.


Client-side Remote Call Interceptors (JAX-RS and RPC)


Sometimes you need a chance to tweak a request just before it's sent to the server. For example, maybe you need to add special authentication headers to certain REST requests. Or maybe you want to short-circuit requests when you already have locally-cached data. Previously, you would have had to give up on the typesafe Errai JAX-RS Caller<T> interface for those requests, and fall back to RequestBuilder.

Well, starting in Errai 2.1, you can intercept, inspect, modify, and even cancel any Errai JAX-RS and Errai RPC request. See the documentation for the details and example code.


GWT 2.5 Compatibility


Errai 2.1 still defaults to GWT 2.4, but we've tested it with the release candidates of GWT 2.5, and we're committed to compatibility. You're free to choose whichever you're most comfortable with.

In Errai 3.0, we do plan to bump up the minimum requirement to GWT 2.5, so get testing!


Code-and-Refresh: Better Than Ever


Code-and-refresh support (Dev Mode) is crucial to a pleasant GWT development experience. We're pleased to say that Errai's code-and-refresh support for CDI is now better than ever. You can make almost any change–even structural changes–to your client-side code, refresh in the browser, and see the results right away.

Additionally, the dynamic (server-side) marshalling system in Errai 2.1 is now run through the same set of tests as the statically-compiled marshallers. This means you can stick to dynamic marshalling at development time, with two big advantages: firstly, it makes dev mode setup simpler (especially when using an external web server); secondly, it behaves well in conjunction with server-side hot reload products such as JRebel.


Errai Marshalling: Now Compatible with Jackson


Speaking of Marshalling enhancements, Errai 2.1 now offers compatibility with the popular Jackson Java-JSON marshalling package. Via the use of a converter, Errai JAX-RS clients can produce requests in a Jackson-compatible format, and can also understand and properly demarshal Jackson payloads in the response. The major use case is that you can now call many pre-existing JAX-RS resource methods from an Errai JAX-RS client. Another possibility is to use Jackson compatibility mode in a new Errai JAX-RS app in order to provide simpler JSON payloads to non-Errai clients. There are a few weaknesses compared with Errai's fully-typed native JSON format, but you can do pretty much everything in Errai's Jackson compatibility mode that you can do with plain Jackson.

The user guide has a section that details how to enable Jackson compatibility mode in the REST client.


Try it

Now that 2.1.0.RC2 is out in the wild, I hope you will take the time to test your Errai app with it. We hope to go final soon, so get those bug reports and pull requests in before it's too late!

And if you're new to Errai, the quickest way from-zero-to-Errai is our Getting Started guide.

Happy hacking!

2 comments:

  1. Hi errai team,

    All the added features impressed me a lot.

    I looked at navigation sources and it does not seem that it is possible to pass parameters in a page transition setting them in the start page and extracting them in the destination page.
    I can do that in gwtp and it is the only feature I really miss (if did not overlook something...).

    I particularly like the template mechanism.

    thanks a lot

    ciao Francesco

    ReplyDelete
  2. I'm trying out Errai Jackson Integration... the docs are really limited. How can I configure a custom mapper ? What exactly do you mean with Jackson support ? How far does this support go ?

    ReplyDelete