2016-06-27 1 views
1

Следуя книге «Изучая Vaadin 7, второе издание», я теперь пытаюсь отобразить простые бобы в таблице. Тем не менее, книга охватывает только использование старого класса java.util.Date. Я пытаюсь показать свойство LocalDate с использованием конвертера.Установка преобразования String в LocalDate в таблице Vaadin

боб (Person) Я пытаюсь для отображения:

public class Person { 

private long id; 
private String firstName; 
private String lastName; 
private LocalDate birthdate; 
private Gender gender; 

// .. GETTERS & SETTERS 

Я написал LocalDateToStringConverter, реализуя com.vaadin.data.util.converter.Converter.

package be.kapture.converters; 

import com.vaadin.data.util.converter.Converter; 

import java.time.LocalDate; 
import java.time.format.DateTimeFormatter; 
import java.time.format.DateTimeParseException; 
import java.util.Locale; 

public class LocalDateToStringConverter implements Converter<String, LocalDate> { 

    @Override 
    public LocalDate convertToModel(String value, Class<? extends LocalDate> targetType, Locale locale) throws ConversionException { 
     DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); 
     try { 
      return LocalDate.parse(value, formatter); 

     } catch (DateTimeParseException ex) { 
      return null; 
     } 
    } 

    @Override 
    public String convertToPresentation(LocalDate value, Class<? extends String> targetType, Locale locale) throws ConversionException { 
     return value.toString(); 
    } 

    @Override 
    public Class<LocalDate> getModelType() { 
     return LocalDate.class; 
    } 

    @Override 
    public Class<String> getPresentationType() { 
     return String.class; 
    } 
} 

В пользовательском интерфейсе, вот фрагмент кода, где я установил конвертер:

... 
Table table = new Table("", container); 
table.setConverter("birthdate", new LocalDateToStringConverter()); 

verticalLayout.addComponent(table); 

«контейнер» быть BeanItemContainer, в котором я ставлю некоторые объекты Person пример. При посещении приложение Vaadin в моем браузере я получаю следующее исключение:

jun 27, 2016 1:56:45 PM org.apache.catalina.core.StandardWrapperValve invoke 
SEVERE: Servlet.service() for servlet [HelloVaadinServlet] in context with path [] threw exception [com.vaadin.server.ServiceException: com.vaadin.ui.Table$CacheUpdateException: Error during Table cache update. Additional causes not shown.] with root cause 
com.vaadin.data.util.converter.Converter$ConversionException: Unable to convert value of type java.time.LocalDate to presentation type class java.lang.String. No converter is set and the types are not compatible. 
    at com.vaadin.data.util.converter.ConverterUtil.convertFromModel(ConverterUtil.java:116) 
    at com.vaadin.ui.AbstractField.convertFromModel(AbstractField.java:736) 
    at com.vaadin.ui.AbstractField.convertFromModel(AbstractField.java:721) 
    at com.vaadin.ui.AbstractField.setPropertyDataSource(AbstractField.java:657) 
    at com.vaadin.ui.Table.bindPropertyToField(Table.java:4140) 
    at com.vaadin.ui.Table.getPropertyValue(Table.java:4109) 
    at com.vaadin.ui.Table.parseItemIdToCells(Table.java:2386) 
    at com.vaadin.ui.Table.getVisibleCellsNoCache(Table.java:2225) 
    at com.vaadin.ui.Table.refreshRenderedCells(Table.java:1745) 
    at com.vaadin.ui.Table.refreshRowCache(Table.java:2691) 
    at com.vaadin.ui.Table.containerItemSetChange(Table.java:4587) 
    at com.vaadin.data.util.AbstractContainer.fireItemSetChange(AbstractContainer.java:242) 
    at com.vaadin.data.util.AbstractInMemoryContainer.fireItemsAdded(AbstractInMemoryContainer.java:1012) 
    at com.vaadin.data.util.AbstractInMemoryContainer.fireItemAdded(AbstractInMemoryContainer.java:994) 
    at com.vaadin.data.util.AbstractInMemoryContainer.internalAddItemAtEnd(AbstractInMemoryContainer.java:884) 
    at com.vaadin.data.util.AbstractBeanContainer.addItem(AbstractBeanContainer.java:533) 
    at com.vaadin.data.util.AbstractBeanContainer.addBean(AbstractBeanContainer.java:598) 
    at com.vaadin.data.util.BeanItemContainer.addItem(BeanItemContainer.java:227) 
    at be.kapture.MyUI.init(MyUI.java:88) 
    at com.vaadin.ui.UI.doInit(UI.java:682) 
    at com.vaadin.server.communication.UIInitHandler.getBrowserDetailsUI(UIInitHandler.java:214) 
    at com.vaadin.server.communication.UIInitHandler.synchronizedHandleRequest(UIInitHandler.java:74) 
    at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41) 
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1409) 
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:364) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) 
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2508) 
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2497) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 

Почему не регистрируя конвертер, чтобы сделать строку в LocalDate преобразования (и наоборот, если это необходимо в какой-то момент)? Я попытался использовать анонимный внутренний класс в качестве параметра в методе Table.setConverter с тем же результатом. Что я здесь делаю неправильно?

UPDATE (по запросу):

Вот полный код UI, где таблица, используются контейнерные и конвертер. Примечание: это проект песочницы «HelloVaadin» без фактической цели. Он настроен специально для таких целей, как эта проблема, пытаясь интегрировать LocalDate Java 8 в проекты Vaadin.

package be.kapture; 

import be.kapture.converters.LocalDateToDateConverter; 
import be.kapture.converters.LocalDateToStringConverter; 
import be.kapture.entities.Person; 
import be.kapture.util.CustomFieldGroupFieldFactory; 
import com.vaadin.annotations.*; 
import com.vaadin.data.fieldgroup.FieldGroup; 
import com.vaadin.data.util.BeanItem; 
import com.vaadin.data.util.BeanItemContainer; 
import com.vaadin.server.VaadinRequest; 
import com.vaadin.server.VaadinServlet; 
import com.vaadin.ui.*; 

import javax.servlet.annotation.WebServlet; 
import java.time.LocalDate; 
import java.util.Arrays; 

import static com.vaadin.data.Property.ValueChangeListener; 


@Theme("mytheme") 
@Widgetset("be.kapture.MyAppWidgetset") 
@PreserveOnRefresh 
@Title("Hello Vaadin!") 

public class MyUI extends UI implements Window.CloseListener { 

    private static final Person person1 = new Person(1L, "John", "DOE", LocalDate.of(70, 1, 1)); 
    private static final Person person2 = new Person(2L, "Jane", "doe", LocalDate.of(70, 1, 1)); 
    private static final Person person3 = new Person(3L, "jules", "winnf", LocalDate.of(48, 11, 21)); 
    private static final Person person4 = new Person(4L, "vincent", "Vega", LocalDate.of(54, 2, 17)); 

    private static final BeanItemContainer<Person> container = new BeanItemContainer<>(Person.class); 

    static { 
     container.addAll(Arrays.asList(person1, person2, person3, person4)); 
    } 

    private final VerticalLayout verticalLayout = new VerticalLayout(); 

    @Override 
    protected void init(VaadinRequest vaadinRequest) { 

     Person person = new Person(1L); 
     person.setFirstName("John"); 
     person.setLastName("Doe"); 
     person.setBirthdate(LocalDate.now()); 

     BeanItem<Person> beanItem = new BeanItem<>(person); 

     FieldGroup group = new FieldGroup(beanItem); 
     group.setFieldFactory(new CustomFieldGroupFieldFactory()); 

     Field<?> id = group.buildAndBind("id"); 
     Field<?> firstName = group.buildAndBind("firstName"); 
     Field<?> lastName = group.buildAndBind("lastName"); 

     Field<?> birthdate = group.buildAndBind("birthdate"); 

     Field<?> gender = group.buildAndBind("gender"); 

//  birthdate.setConverter(new LocalDateToDateConverter()); 
//  birthdate.setPropertyDataSource(item.getItemProperty("birthdate")); 
//  FormLayout layout = new FormLayout(id, firstName, lastName, 
//    birthdate); 
//  layout.setMargin(true); 
//  setContent(layout); 

     verticalLayout.setMargin(true); 
     verticalLayout.setSpacing(true); 

     verticalLayout.addComponents(id, firstName, lastName, birthdate, gender); 

     // Define a person which cannot exist 
     Person nullPerson = new Person(-1L); 
     nullPerson.setFirstName("Test"); 
     container.addItem(nullPerson); 
     final ListSelect select = new ListSelect("", container); 

     // Send events on directly when clicked 
     select.setImmediate(true); 

     // Handle the value of the person as null 
     select.setNullSelectionItemId(nullPerson); 
     select.setItemCaptionPropertyId("firstName"); 
     select.addValueChangeListener((ValueChangeListener) event -> System.out.println(select.getValue())); 

     verticalLayout.addComponent(select); 

     Table table = new Table(""); 
     table.setEditable(true); 
     table.setConverter(LocalDateToDateConverter.class); 
     table.setContainerDataSource(container); 

     verticalLayout.addComponent(table); 

     setContent(verticalLayout); 
    } 

    @Override 
    public void windowClose(Window.CloseEvent e) { 
     Notification.show("Window closed."); 
    } 

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true) 
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false) 
    public static class MyUIServlet extends VaadinServlet { 
    } 
} 
+0

Вы установили преобразователь перед установкой контейнера? –

+0

@ A.Meier, контейнер задается в параметре конструктора таблицы. Кажется, я вижу ошибку сейчас. Сначала должен быть установлен преобразователь, а затем источник данных? –

+0

Это правда. Источник данных должен быть установлен после конвертера. –

ответ

1

Мне было любопытно и выяснить вашу проблему. Метод setConverter(Object, Converter) просто позволяет изменить способ отображения значений в этом столбце пользователю (текстовое представление). Вот почему тип второго аргумента - Converter<java.lang.String,?>.

В вашем примере вы включили редактирование в таблице. Это требует, чтобы Ваадин знал, как он предоставит редактор таблицы для вашего столбца LocalDate. По умолчанию таблица не знает о LocalDate. Я отдаю себе отчет в 2-х вариантов у вас есть:

  1. Реализовать converter factory, который возвращает Converter для LocalDate к String. Затем компонент таблицы может отображать текстовое поле, в котором вы можете ввести дату в соответствии с форматом конвертера. Я не пытался, что произошло, когда пользователь вводит недопустимую строку.
  2. Внесите a table field factory и позвоните setTableFieldFactory на компоненте таблицы. На полевой фабрике будет отображаться PopupDateField или что-то подобное. Таким образом, вы являетесь безопасным по типу и можете использовать встроенные поля даты.

IMO последнее могло бы стать лучшим пользовательским интерфейсом, но, конечно, это больше усилий в разработке.

+0

Я считаю, что ваш ответ на месте, сэр Харбич. После комментирования вызова setEditable пользовательский интерфейс генерируется без ошибок. Поля LocalDate отображаются в таблице в простом текстовом поле. TableFieldFactory кажется не таким легким, как первая мысль. Полагаю, для преобразования его в PopupDateField вам понадобится конвертер от LocalDate to Date? Я не вижу, как Ваадин мог отображать неподтвержденный тип даты в своих готовых компонентах в противном случае. –

+1

Правильно, вам нужно реализовать конвертер из LocalDate (model) to Date (презентация) и установить этот конвертер в PopupDateField, который можно было бы сделать в TableFieldFactory. –

+1

Я получил код для работы с редактируемыми полями теперь благодаря вашему входу Steffen! В качестве справочника для будущих читателей: реализуйте TableFieldFactory и конвертер . В переопределенном методе createField на вашем заводе: проверьте, если (LocalDate.class.isAssignableFrom (property.getType())) создайте новый PopupDateField (или что хотите), по методу SetConverter с полем/компонентом с вашим конвертером, вызовите setPropertyDatasource с правильным источником данных и вернуть его! Для всех других типов делегируйте создание в DefaultFieldFactory.get(). CreateField. –