joist-web pages are objects that implement the Page interface.

Pages are responsible for initializing controls to render the page and handle any user interaction on form submissions.


joist-web maps class names from URLs simply:

Although you can override this mapping in the PageResolver class.


This is the code-behind Page class for foo.htm, a data-entry page for Foo objects:

    public class FooPage extends AbstractProjectPage {

        // When hitting foo.htm?foo=1, Foo with id=1 is put into this field
        public Foo foo;
        public Form form = new Form("form");

        public void onInitt() {
            if ( == null) {
                // They hit the "new" button instead of the "edit" button
       = new Foo();

            // Define the form using the bindgen-generated bindings
            FooPageBinding b = new FooPageBinding(this);
            this.form.add(new TextArea(;
            this.form.add(new TextArea(;
            this.form.add(new SubmitButton(;

        // In AbstractProjectPage, DomainObjects are not allowed to be set into any public field
        // unless this method explicitly allows them--prevents users from screwing with URLs
        public boolean isAllowedViaUrl(DomainObject instance) {
            return instance instanceof Foo && ((Foo) instance).getUser().equals(this.currentUser.get());

        // Called when the user hit's submit
        public void save() {
            if (this.commit()) {
                this.messages.addInfo("Successfully saved {}",;

And now the foo.htm, which is a Velocity template:


Across lots of pages, the succinctness of the foo.htm page and the type-safety of the bindgen-based Form definition should be a boon to productivity.