Saturday, May 14, 2016

Uberfire - The Unanswered Questions: Part 6

TO-DO: Make a TO-DO List

In this session I'll be creating an editor for the TO-DO Task object. I'll start off by duplicating the Presenter-View pattern already established by the other two popups for NewFolder and NewProject. The reason for this is because I want to explore some new GWT widgets for my Task Editor and I don't want to complicate things by adding all of the other junk required to support a WorkbenchEditor. In this round we'll just create a modal popup dialog to host the Task Editor but eventually we may want to place it somewhere in the workbench window.

This first step is dead simple: copy and rename the three files named NewFolder<something> in the org.uberfire.client.screens.popup package and replace "NewFolder" with "TaskEditor". So, you should have TaskEditorPresenter,java TaskEditorView.java and TaskEditorView.html and they should look something like this:

TaskEditorPresenter.java

@Dependent
public class TaskEditorPresenter {

    public interface View extends UberView<TaskEditorPresenter> {

        void show();

        void hide();
    }

    @Inject
    private View view;

    private TasksPresenter tasksPresenter;

    @PostConstruct
    public void setup() {
        view.init( this );
    }

    public void show( TasksPresenter tasksPresenter ) {
        this.tasksPresenter = tasksPresenter;
        view.show();
    }

    public void close() {
        view.hide();
    }
}

TaskEditorView.java

@Dependent
@Templated
public class TaskEditorView extends Composite
        implements TaskEditorPresenter.View {
    
    private TaskEditorPresenter presenter;

    private Modal modal;
    
    @Inject
    @DataField("ok-button")
    Button okButton;

    @Inject
    @DataField("cancel-button")
    Button cancelButton;

    @Override
    public void init( TaskEditorPresenter presenter ) {
        this.presenter = presenter;

        this.modal = new Modal();
        final ModalBody body = new ModalBody();
        body.add( this );
        modal.add( body );
    }

    @Override
    public void show() {
        modal.show();
    }

    @Override
    public void hide() {
        modal.hide();
    }

    @EventHandler("ok-button")
    public void onOk( ClickEvent event ) {
        presenter.close();
    }

    @EventHandler("cancel-button")
    public void onCancel( ClickEvent event ) {
        presenter.close();
    }
}

TaskEditorView.html

<div>
     <form data-field="task-editor-modal">
        <fieldset>
            <legend>Task Editor</legend>
            <div class="form-group">
            </div>
        </fieldset>
    </form>
    <div class="modal-footer">
        <button type="button" class="btn btn-danger" data-field="cancel-button">Cancel</button>
        <button type="button" class="btn btn-primary" data-field="ok-button">OK</button>
    </div>
</div>

Then in TasksPresenter.java add this:

import org.uberfire.client.screens.popup.TaskEditorPresenter;

    @Inject
    private TaskEditorPresenter taskEditorPresenter;

    public void showTaskEditor(Task task) {
        taskEditorPresenter.show(this);
    }

and in TasksView.java change the generateTasks() method to this:

    private ListGroupItem generateTask(Task task) {
        TaskItem taskItem = new TaskItem(task);
        taskItem.add(createTaskCheckbox(task));
        taskItem.add(createTaskNotesButton(task));

        return taskItem;
    }

This will append a button with an EDIT icon to the name in the task list. Finally add the createTasksNotesButton method:

import org.gwtbootstrap3.client.ui.constants.IconType;

    private Button createTaskNotesButton(Task task) {
        Button button = GWT.create(Button.class);
        button.setIcon(IconType.EDIT);
        button.setMarginLeft(20.0);
        button.addClickHandler(event -> presenter.showTaskEditor(task));
        return button;
    }

Rebuild the whole mess and run it - you should that the tasks in the list now have an EDIT icon next to them.


When you click that icon you'll just get an empty modal popup dialog with an "OK" and "Cancel" button. And now, it's time for...

Fun With Widgets!

At the beginning of this series I mentioned that a Task should have more than just a brief description (the "name" attribute) mostly because I needed an excuse to build on an already freaking awesome web app. It would be nice to be able to add some notes, possibly in the form of a Rich Text document with 4x8 color glossy pictures and circles and arrows and such. I also need to be able to prioritize my TO-DO list, so let's assign a numeric priority value to each Task. Finally, I would like to know when a Task needs to be completed, so each Task needs a due date.

If all this sounds a bit contrived, that's because it is - it's an excuse to have some Fun With Widgets!

Rich Text

The GWT Rich Text Area is a text editor that allows complex styling and formatting. This widget is browser dependent and may not work as expected for all browsers.

We'll use this for our Task notes field, so let's go ahead and add one of these to our popup. First, add an anchor to TaskEditorView.html:

     <form data-field="task-editor-modal">
        <fieldset>
            <legend>Task Editor</legend>
            <div class="form-group">
                <a data-field="task-notes"></a>
            </div>
        </fieldset>
    </form>

Then create the Rich Text Area in TaskEditorView.java and attach it to the anchor:

    RichTextArea notesTextArea;
    
    @Inject
    @DataField("task-notes")
    Anchor taskNotesAnchor;

    @Override
    public void init( TaskEditorPresenter presenter ) {
        ...

        // add the other widgets
        notesTextArea = new RichTextArea(); 
        notesTextArea.setHeight("200");
        notesTextArea.setWidth("100%");
        notesTextArea.setHTML("<b>Hello World!</b><br/>Uberfire is cool");
        taskNotesAnchor.add(notesTextArea);
    }

List Box (a.k.a. "ComboBox")

This is general-purpose selection list which can either be the drop-down "Combo Box" style, or simply a list of selections. We're going to use this for our Task priority field. Again, in the html template, add an anchor for our ListBox widget:


     <form data-field="task-editor-modal">
        <fieldset>
            <legend>Task Editor</legend>
            <div class="form-group">
                <a data-field="task-notes"></a>
                <b>Priority:</b> <a data-field="task-priority"></a>
            </div>
        </fieldset>
    </form>

And in the View class:

    @Inject
    @DataField("task-priority")
    Anchor taskPriorityAnchor;

    ListBox priorityListBox;

    @Override
    public void init( TaskEditorPresenter presenter ) {
        ...

        priorityListBox = new ListBox();
        priorityListBox.setMultipleSelect(false);
        priorityListBox.addItem("Medium", "2");
        priorityListBox.addItem("High", "3");
        priorityListBox.addItem("Low", "1");
        priorityListBox.addItem("Screw it, I don't time for this crap!", "0");
        taskPriorityAnchor.add(priorityListBox);
    }

Date Picker

Finally, let's add a Date Picker widget for the Task due date. In the html template add an anchor for the widget:

     <form data-field="task-editor-modal">
        <fieldset>
            <legend>Task Editor</legend>
            <div class="form-group">
                <a data-field="task-notes"></a>
                <b>Due Date:</b> <label data-field="task-due-date-label">Due Date</label> <a data-field="task-due-date"></a>
                <b>Priority:</b> <a data-field="task-priority"></a>
            </div>
        </fieldset>
    </form>

And add the relevant bits in the View class:

    public static class DueDateValueChangeHandler implements ValueChangeHandler<Date> {
        private final Label text;

        public DueDateValueChangeHandler(Label text) {
            this.text = text;
        }

        public void onValueChange(ValueChangeEvent<Date> event) {
            Date date = event.getValue();
            String dateString = DateTimeFormat.getFormat(PredefinedFormat.DATE_MEDIUM).format(date);
            text.setText(dateString);
        }
    }
   
    @Inject
    @DataField("task-due-date")
    Anchor taskDueDateAnchor;

    @Inject
    @DataField("task-due-date-label")
    Label dueDateLabel;

    DatePicker dueDatePicker;

    @Override
    public void init( TaskEditorPresenter presenter ) {
        ...

        dueDatePicker = new DatePicker();
        dueDatePicker.setYearArrowsVisible(true);
        dueDatePicker.setYearAndMonthDropdownVisible(true);
        dueDatePicker.setVisibleYearCount(51);
        dueDatePicker.addValueChangeHandler(new DueDateValueChangeHandler(dueDateLabel));
        dueDatePicker.setValue(new Date(), true);
        taskDueDateAnchor.add(dueDatePicker);
    }

Note the internal class DueDateValueChangeHandler. This is called when the user selects a date from the DatePicker and updates  the Due Date label in the html template.

Stylings

The default DatePicker presentation is pretty lame so I've added a stylesheet to make it look a bit better. A note about CSS or stylesheets: If you create a ".css" file with the same name as your html template file, Errai will automatically load that file and apply the styles to the widgets in your template. In this case, I have defined some styles for the gwt-DatePicker in a file named TaskEditorView.css.

You can also specify a stylesheet using @Templated(stylesheet=“some/other/path/stylesheet.css”) in your Viewer java class.

Also, the usual stylesheet inclusion methods still work in Errai: any styles or CSS files included in your src/main/webapp/index.html or src/main/webapp/login.jsp will be available to the entire application.

I'll save you the boring details of TaskEditorView.css here; instead grab the latest tag from my repository and enjoy.

Let 'er Rip!

Let's build and run this sucker and see what happens. If everything went right, you should see this popup when you click on one of the Task EDIT buttons:


What About WorkbenchEditor?

I know that last time I promised to dive deeper into the inner workings of a WorkbenchEditor, but I think it was important to explore the relationship between the html template and view and have some Fun With Widgets! I promise to show you how WorkbenchEditor works in Part 7 of this series.

3 comments:

  1. One thing we actually need is a WYSIWYG Rich Text Editor Screen, which is missing from our project.

    Just found this, quite liked it. As it's simple, fairly powerful, does tables and is designed to be skinned (others have their own skin and can ook out of place). It's also liberal MIT licensed, which is a plus:
    https://github.com/Voog/wysihtml

    You'd need to use jsni (speak to eder), this time around, to integrate a JS component. Which is a little cumbersome. But once we move to GWT2.8, you can use JsInterop, which is annotation based overlays.
    http://www.luigibifulco.it/blog/en/blog/gwt-2-8-0-jsinterop

    ReplyDelete
  2. I am planning migrating a Eclipse RCP app across to uberfire. To do this in bite sized chunks I am planning to replace it's current CDO repository with VFS. One thing that is puzzling me is if I can use make my 'client' a Java app rather than a gwt javascript app. Any pointers on how to use the API for this use case? I'll need to persist groovy and drools scripts , bpmn2 xml and Ecore xmi files, all version dependent on each other.

    ReplyDelete
  3. I don't think that's possible because all Java code is "compiled" to javascript by GWT, so either way you get javascript. You might want to post your question to the #uberfire IRC chat on irc.freenode.net to get some ideas about a possible solution.

    ReplyDelete