Первые шаги к освоению Vaadin

16 мая 2011 г.

Логотип Vaadin

Сегодня я хотел бы рассказать о таком framework(e), как Vaadin. На просторах интернета, к сожалению, можно найти только одну статью посвященную ему Изучая Vaadin, и та, являются исключительно ознакомительной. Конечно, существует официальная документация, а так же достаточно большая коммунити, посвещенная данному framework(y), но, к сожалению, вы не всегда можете сразу найти некоторые особенности «продукта», который собираетесь использовать, что в дальнейшем может заставить вас отказаться от него, так как он покажется вам слишком «неудобным».

Vaadin }>

Прежде, чем перейти к основной части статьи хотелось бы рассказать вам немного о логотипе Vaadin. Логотип состоит их двух всем хорошо знакомых символов: } — фигурные скобки символизируют собой принадлежность Vaadin(a) к JAVA, а > — языку разметки HTML. Таким образом логотип объединяет в себе две технологии.

Основы основ

Начать я думаю необходимо с основ, то есть, как сделать простое приложение, вдаваться в подробности тут не стоит, так как этому посвещена очень хорошая статья на официальном сайте Vaadin, которую, можно найти перейдя по данной ссылке, но сразу хотел бы обратить ваше внимание на то, что будет необходимо внести небольшое изменение в класс SMSApp, а именно заменить implements ApplicationContext.TransactionListener на implements HttpServletRequestListener, причину данного изменения вы можете прочитать в данной статье. Хотелось бы пояснить, для чего необходим HttpServletRequestListener. Рассмотрим пример:

..
@Override
public void init() {
setInstance(this);final Window mainWindow = new Window();
mainWindow.setImmediate(true);

setTheme("myApp");
mainWindow.setCaption("Application title");

// Need to center main window on page
final VerticalLayout mainWindowLayout = new VerticalLayout();
mainWindowLayout.setSizeFull();
mainWindowLayout.addComponent(createAppMainPanel());
mainWindow.setContent(mainWindowLayout);
setMainWindow(mainWindow);
}

/*
* Главная панель данного приложения
*/
private Panel createAppMainPanel() {
Panel panel = new Panel();

GridLayout layout = new GridLayout();
panel.setSizeFull();
panel.setStyleName(Runo.PANEL_LIGHT);
HorizontalLayout mainLayout = new HorizontalLayout();
mainLayout.setSizeFull();

layout.setColumns(1);
layout.setRows(3);
layout.setSpacing(true);
layout.setWidth("985px");

layout.addComponent(new ApplicationHeader(), 0, 0);
layout.addComponent(new LoginFormPanelLayout());
layout.addComponent(new ApplicationFooter(), 0, 2);

mainLayout.addComponent(layout);
mainLayout.setComponentAlignment(layout, Alignment.TOP_CENTER);

panel.addComponent(mainLayout);

return panel;
}

В классе LoginFormPanelLayout будет происходить авторизация пользователя. Предположим, что вы будет проверять введенные пользоватем данные с данным в вашей базе данных и для этого вы написали некий ClientServicе с методом authenticate, для получения доспута к данному сервису вам необходимо будет воспользоваться SpringContextHelper, о создании, которого можно прочитать в статье Spring Integration, и единственным обязательным параметром, который необходимо в него передавать, является вашe приложение (MyApplication), но так как на данный момент компонент (LoginFormPanelLayout) только начал инициализироваться, то есть еще не был «прикреплен» к самому приложению, вы не сможете воспользоваться методом getApplication() для получения переменной MyAppication, именно для этого необходим HttpServletRequestListener. Конечно, можно возразить и сказать, что достаточно просто передавать переменную приложения (MyApplication) в конструктор класса. Конечно, можно сделать и так, но тогда при создании каждого нового компонента, где будет необходимо использовать spring(овые) bean(ы) вам будет необходимо передавать в коструктор данную переменную, что, с моей точки зрения, является не совсем «красивым» решением. Также можно предложить иницилизировать необходимый класс только после того, когда компонент был «прикреплен» к вашему приложению. Подобный подход тоже имеет право на существование, давайте рассмотрим как это можно реализовать на примере нашего класса.

public class LoginFormBean {@NotNull(message = "Username is required!")
private String username;
@NotNull(message = "Password is required!")
private String userpass;
//Getters && setters
}

public LoginFormPanelLayout extends Panel () {
//Вернет текущее приложение
MyApplication app = MyApplication.getInstance();
//Вернет null, соответсвенно получим NullPoint при выполнении getBean("clientService");
MyApplication app = this.getApplication();

SpringContextHelper springContextHelper = new SpringContextHelper(app);
ClientServicе clientService springContextHelper.getBean("clientService");

GridLayout loginPanelLayout = new GridLayout(1, 3);
loginPanelLayout.setSizeFull();

final BeanValidationForm loginForm = new BeanValidationForm(LoginFormBean.class);

loginForm.setFormFieldFactory(cf.createLoginFormFieldFactory());

loginForm.setItemDataSource(new BeanItem(new LoginFormBean()));

loginForm.setImmediate(true);
loginForm.setVisibleItemProperties(new String[] { "username", "userpass" });
Button loginButton = new Button("login");
loginButton.setImmediate(true);
loginButton.setClickShortcut(KeyCode.ENTER);
loginPanelLayout.addComponent(loginForm, 0, 0);
loginPanelLayout.addComponent(loginButton, 0, 2);

loginButton.addListener(new Button.ClickListener() {
@Override
public void buttonClick(final ClickEvent event) {
// В данном случае мы получим наше приложение, то есть, вызывая, springContextHelper здесь не будет nullPoint(а), так как компонент был полность создан и положен в контект самого приложения
MyApplication application = getApplication();
}
});
addComponent(loginPanelLayout);
}

Примерно так будет выглядеть наша форма логина.
Форма входа
Однако хотелось бы заострить ваше внимание на том, что данный прием будет действинен только в тех случаях, когда серсив будет вызываться после выполнения неких действий со стороны пользователя, если же вам необходимо уже при самой иницилизации компонента получить доступ к сервису, к примеру, для получения данных на основе которых будет построена таблица или что-то еще, тогда вы должны будете либо передавать в конструктор ваше приложение или воспользоваться HttpServletRequestListener.

Индикатор ожидания

Еще одну вещь, которую хотелось бы затронуть в рамках данной статьи – процесс ожидания выполнения какой либо операции. Если вы посетили примеры ресурсов, написанных c использованием VAADIN framework(a), указанные в данном топике, то обратили внимание, что при загрузки страницы появился небольшой индикатор ожидания, он же будет появляться в верхнем правом углу при каждом действии пользователя, которое сопровождается изменение в UI или загрузки данных. Почему же именно об индикаторе ожидания я хотел поговорить? Многие пользователи не всегда внимательны при использовании приложений и в один момент может возникнуть ситуации, когда пользователь не будет понимать в каком «статусе» сейчас находится приложение, в некоторых случаях, когда процесс ожидания не какой долгий, этот факт можно проигнорировать, однако, если пользователь находясь в вашей приложении попытается открыть страницу, на которой будет выполнен «тяжелый» запрос к базе данных или должно загрузиться большое количество компонентов, то в этом случае наиболее понятным для пользователя будет «большой» индикатор ожидания, который будет блокировать любые его действия, пока текущия страница не будет проиницилизирована (конечно, всегда можно добавить возможность прервать выполнение запущенного задания). Давайте рассмотрим, как это можно реализовать в нашел случае.

..
loginButton.addListener(new Button.ClickListener() {
@Override
public void buttonClick(final ClickEvent event) {final Window loadingWindow = new LoadingWindow();
//Добавляет loading window
application.getMainWindow().addWindow(loadingWindow);

layout.removeComponent(0, 0);
VerticalLayout errorLayout = new Verticatllayout();
layout.addComponent errorLayout, 0, 0);

Thread workThread = new Thread() {
@Override
public void run() {
try {
loginForm.validate();
loginForm.commit();

try {
String username = loginForm.getItemProperty("username").getValue();
String password = loginForm.getItemProperty("userpass").getValue();
clientService. authenticate(username, password);

//TODO: Здесь будет проиницилизирован новый компонент, если пользователь ввел верные данные

} catch (ConnectException e) {
errorLayout.addComponent(new Label(„Connect error“));
} catch (BadCredentialsException e) {
errorLayout.addComponent(new Label(„Wrong username/password“));
} finally {
application.getMainWindow().removeWindow(loadingWindow);
}
}
};

} catch (InvalidValueException e) {
errorLayout.addComponent(new Label(e.getMessage());
application.getMainWindow().removeWindow(loadingWindow);
}
workThread.start();
}
});

/*
* Modal loading window
*/
public class LoadingWindow extends Window {

public LoadingWindow() {
this.setModal(true);
this.center();
this.setStyleName("loading-window");
this.setClosable(false);

ProgressIndicator loadingIndicator = new ProgressIndicator();
loadingIndicator.setStyleName("preload-spinner");

this.addComponent(loadingIndicator);
}
}

В данном примере показана реализация индикатора ожидания, в нашем случае это будет модальное окно внутри которого показывается индикатор ожидания, в случает каких либо ошибок будет отображено соответствующее сообщение.

Заключение

В рамках одной статьи достаточно тяжело описать все ньюансы работы с данным framework(ом). Но я надеюсь кто-то найдет данную статью полезной для себя и начнет изучать Vaadin.

Теги: рубрика Java, Сайтостроение