Part 3 :: Show and edit data in a popup window

When double-clicking an entry in the grid, we want to show all details in a popup window. This window can be used also to create new entries and to change existing ones.

Create a new class called DocumentDataView and extend the com.vaadin.ui.Window class.

package net.stephankopp;

import java.util.ArrayList;
import java.util.List;

import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.DateField;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;

public class DocumentDataView extends Window {
    private DocumentData documentData;

    /**
    * Constructor
    * @param documentData The data object we want to display
    */
    public DocumentDataView(DocumentData documentData) {
        this.documentData = documentData;

        //Create a form layout which holds all components
        FormLayout layout = new FormLayout();
        layout.setMargin(true);
        layout.setSpacing(true);

        //Status combobox
        ComboBox<String> status = new ComboBox<String>();
        status.setCaption("Status");
        List<String> statusList = new ArrayList<String>();
        statusList.add("OPEN");
        statusList.add("IN PROGRESS");
        statusList.add("CLOSED");
        status.setItems(statusList); //configure the possible options (usually, this is not a manually defined list...)
        if (documentData.getStatus() != null) status.setValue(documentData.getStatus()); //load the value of given data object
        layout.addComponent(status);

        //Due Date DatePicker
        DateField dueDate = new DateField();
        if (documentData.getDueDate() != null) dueDate.setValue(documentData.getDueDate());
        dueDate.setCaption("Due date");
        layout.addComponent(dueDate);

        //Topic TextField
        TextField topic = new TextField();
        topic.setCaption("Topic");
        if (documentData.getTopic() != null) topic.setValue(documentData.getTopic());
        layout.addComponent(topic);

        //Description TextArea
        TextArea description = new TextArea();
        description.setCaption("Description");
        if (documentData.getDescription() != null) description.setValue(documentData.getDescription());
        layout.addComponent(description);

        //Add save and cancel buttons
        HorizontalLayout buttonLayout = new HorizontalLayout();
        Button save = new Button("save");
        save.setStyleName("friendly");
        buttonLayout.addComponent(save);
        Button cancel = new Button("cancel");
        cancel.addClickListener(event -> this.close());
        buttonLayout.addComponent(cancel);
        layout.addComponent(buttonLayout);

        //Set layout size to undefined to let it fit to its content
        layout.setSizeUndefined();

        //Set the layout as content of the hole window
        setContent(layout);
        
        //Configure position and caption of the window
        setCaption("Entry Details");
        center();
    }
}

Change your class VaadinUI to open the new window on a double click to a grid entry. This can be done with a listener added to the grid as shown in the code:

//Add a grid, which displays all our data and add it to the layout
dataGrid = new Grid<DocumentData>();
dataGrid.setWidth("100%");
dataGrid.setHeight("100%");
layout.addComponent(dataGrid);

//Add a double-click listener
dataGrid.addItemClickListener(event -> {
	//Check, if it is a double-click event
	if (event.getMouseEventDetails().isDoubleClick()) {
		//get the item which has been clicked
		DocumentData documentData = event.getItem();
		//open the item in a window
		DocumentDataView window = new DocumentDataView(documentData);
		getUI().addWindow(window);
		//add a listener, which will be executed when the window will be closed
		window.addCloseListener(closeEvent -> {
			updateGrid(); //refresh grid to show any changes
		});
	}
});

Go back to your class DocumentDataView. The cancel button has already its functionality to simply close the window. Now lets implement the save button.
It’s quite easy. When opening the window, we give it the DocumentData as parameter to read and display its values. To save the data, we just have to write them back to the object.

//Add save and cancel buttons
HorizontalLayout buttonLayout = new HorizontalLayout();
Button save = new Button("save");
save.setStyleName("friendly");
buttonLayout.addComponent(save);
Button cancel = new Button("cancel");
cancel.addClickListener(event -> this.close());
buttonLayout.addComponent(cancel);
layout.addComponent(buttonLayout);

//save click listener
save.addClickListener(event -> {
	//TODO: Do some validations here
	
	//Write your changes back to the object
	documentData.setStatus(status.getValue());
	documentData.setDescription(description.getValue());
	documentData.setDueDate(dueDate.getValue());
	documentData.setTopic(topic.getValue());
	
	//Mark the object as changed
	documentData.setChanged(true);
	
	//Close window
	close();
});

Remember the window close listener, we have created in the VaadinUI class after opening the new window. When saving and closing, this listener automatically refreshes the grid and shows the changed data. This change is not yet persistent, as soon as you restart your application, it is gone. When closing your browser, it will be available during next session and also for any other user accessing this application. Our service class DatabaseConnector will be shared over multiple sessions and exists only once.

Two functions are still missing, but easy to implement, the delete and create buttons in the VaadinUI class. At first, we have to create the necessary backend functions in the class DatabaseConnector. Create a new method called delete:

/**
* Deletes the given documentData object
* @param documentData
*/
public void delete(DocumentData documentData) {
	//Remove the object from the cachedData list
	cachedData.remove(documentData);
}

We also need an “add” method, which adds a new documentData object:

/**
* Adds the given documentData object
* @param documentData
*/
public void add(DocumentData documentData) {
	//add the given DocumentData to the cachedData list
	cachedData.add(documentData);
}

Now we have our backend functionality and we can use it in our VaadinUI class.
At first, we have to change the grid to allow multi-selections. This is a simple option we can set in our init method:

//Change grid to allow multi-selections
dataGrid.setSelectionMode(SelectionMode.MULTI);

Add the button layout, button and click listener to the delete button:

//Create a separate layout containing the buttons and add it to the layout
HorizontalLayout buttonLayout = new HorizontalLayout();
buttonLayout.setSpacing(true);
layout.addComponent(buttonLayout);

//Add a button to delete selected entries and add it to the layout
Button delete = new Button("delete");
delete.addClickListener(event -> {
	//Get a list of selected documentData objects
	Set<DocumentData> selected = dataGrid.getSelectedItems();
	//loop through all selected objects and delete them
	for (DocumentData d : selected) {
	   databaseConnector.delete(d);
	}
	//refresh grid
	updateGrid();
});
buttonLayout.addComponent(delete);

Add a click listener to the create button. This listener creates an empty documentData object and after that it is the same procedure as showing an existing item. The difference is the window close listener. In this listener, we check if the user hit the save button and only then, we create a new object in the databaseConnector class.

//Add a button to create new entries and add it to the layout
Button create = new Button("create");
create.addClickListener(event -> {
	//Create a new empty DocumentData object
	DocumentData documentData = new DocumentData();
	//open the new item in a window
	DocumentDataView window = new DocumentDataView(documentData);
	getUI().addWindow(window);
	//add a listener, which will be executed when the window will be closed
	window.addCloseListener(closeEvent -> {
		//check if "changed" property of DocumentData object is true, which means user hit the save button
		if (documentData.isChanged()) {
			//add the newly created object
			databaseConnector.add(documentData);
		}
		//refresh grid to show any changes
		updateGrid(); 
	});
});
buttonLayout.addComponent(create);

That’s it, we now have a demo application, which can show, edit, delete and create data entries.
In part 4, we will talk about the REST API, which is necessary to communicate with our existing Notes application.