Как сделать не обязательный выпадающий список в Spring Roo / Dojo? - PullRequest
3 голосов
/ 12 августа 2011

В Spring Roo (1.1.5) у меня есть объект "Книга", который МОЖЕТ иметь ссылку на объект "Издатель".

class Book {
   @ManyToOne(optional=true)
   Publisher publisher
}

Теперь у меня есть сгенерированный Roo контроллер и файлы JSPX. В графическом интерфейсе для создания и обновления книги есть сгенерированный Roo выпадающий список (выделен dijit.form.FilteringSelect) для выбора издателя. Но пользователь ДОЛЖЕН выбрать издателя; «Пустого» поля нет!

Моя первая попытка состояла в том, чтобы просто добавить значение null в список, который представляет опции для выпадающего списка. Но это не удалось. (java.lang.IllegalArgumentException: Bean object must not be null) - Так что это может быть неправильным путем.

Поэтому, прежде чем я попытаюсь самостоятельно расширить файл select.tagx, я хочу спросить , если кто-то уже решил эту проблему (с дополнительным выпадающим списком в Spring Roo / Dojo), или я что-то делаю полностью неправильно и это должно работать в нормальном случае, не вызывая чего-то нового?

Ответы [ 2 ]

3 голосов
/ 18 августа 2011

Я нашел решение, но оно работает только потому, что мое приложение больше не является стандартным приложением Roo.В любом случае, я объясню свое решение, может быть, кто-то найдет способ адаптировать его для стандартных приложений Roo.

Идея состоит в том, чтобы добавить пустой выбор в раскрывающемся списке, когда атрибут required равен false.Основная проблема заключается в том, что расширение dijti / dojo не будет работать правильно, если в раскрывающемся списке есть одна опция без value.Поэтому я решил дать им, например, value "null" (<option value="null></option>).На стороне сервера нужно немного изменить преобразователь, который преобразует идентификатор базы данных (то есть нормальное значение) в сущность (путем загрузки его из базы данных), чтобы он преобразовывал строку "null" в null вместосущность.

Но это проблема с пружиной Ру.Roo использует org.springframework.core.convert.support.IdToEntityConverter, который автоматически регистрируется (не документировано https://jira.springsource.org/browse/SPR-7461)) и будет пытаться преобразовать каждый объект в сущность, если класс сущности используется как метод статического поиска. Я не нашел способа изменить его поведение.

Но лично мне очень повезло, потому что некоторое время назад я изменил свое приложение, так как оно не имеет этого статического искателя, поэтому у меня есть свой собственный общий преобразователь Id в сущность, который легко изменить.String to Entity. Если String имеет значение «null», он возвращает ноль, иначе он преобразует String в число и загружает Entity по этому номеру / id.

Для представления он показывает, что нужно расширитьфайл select.tagx.

Файл select.tagx содержит 12 различных способов заполнения поля выбора.

  • 6 из них предназначены для множественного выбора, поэтому они могут оставаться такими же, как ониis.
  • 2, если не несколько из них предназначены для отключенной привязки формы, необходимо добавить этот блок сразу после тега select

, строка 75, 130,

<c:if test="${not required}">
    <option value="null"></option>
</c:if>
  • остальные 4 немного сложнее

...

<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" />
 ...
 <form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemLabel="${sec_itemLabel}"/>
 ...
<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" />
 ...
<form:select id="_${sec_field}_id" items="${items}" path="${sec_field}" disabled="${disabled}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>

Им нужно заменить полный тег на( Я продемонстрирую это только для последних 4, но остальные похожи, за исключением того, что нужно удалить параметр itemVlaue и / или itemLabel )

<form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}">                          
    <c:if test="${not required}">                               
       <option value="null"></option>
    </c:if>
    <form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>
</form:select>

Теперь это должно работать.


Но у него есть небольшой недостаток.Если есть Книга без Издателя, то в пустом раскрывающемся списке не будет атрибута выбора.Это не так уж плохо, потому что это самый верхний вариант и будет отображаться, если не выбран ни один другой вариант.

Если кто-то не может принять этот недостаток, то один из способов решить эту проблему - написать собственныйРасширение тега jsp org.springframework.web.servlet.tags.form.Option (класс, который делает тег опции весны).Есть только две вещи, которые действительно нужно изменить:

1) метод isSelected(Object resolvedValue) должен возвращать true, если состояние привязки нулевое (поэтому этот метод становится действительно простым)

private boolean isSelected(Object resolvedValue) {
  BindStatus bindStatus = getBindStatus();  
  return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null;
}

2) если тег отображается без или с пустым телом (метод renderDefaultContent), содержимое отображаемого html option должно быть пустым, но не value.Таким образом, второй параметр метода renderOption (SpecialWay) должен быть установлен на пустую строку.

@Override
protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
  Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME);    
  renderOptionSpecialWay(value, "", tagWriter);
}

Но поскольку метод isSelected является закрытым и не может быть переопределен, необходимо скопировать renderOption (может переименовать его) и должен изменить его так, чтобы он вызывал «новый» метод isSelected.То же самое должно быть сделано с двумя методами renderDefaultContent и renderFromBodyContent, потому что renderOption тоже является приватным.

Итак, один придумал этот класс:

public class NullOptionTag extends OptionTag {

  @Override
  protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
    Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME);
    renderOptionSpecialWay(value, "", tagWriter);
  }

  @Override
  protected void renderFromBodyContent(BodyContent bodyContent, TagWriter tagWriter) throws JspException {
   Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME);
   String label = bodyContent.getString();
   renderOptionSpecialWay(value, label, tagWriter);
  }

  private void renderOptionSpecialWay(Object value, String label, TagWriter tagWriter) throws JspException {
    tagWriter.startTag("option");
    writeOptionalAttribute(tagWriter, "id", resolveId());
    writeOptionalAttributes(tagWriter);
    String renderedValue = getDisplayString(value, getBindStatus().getEditor());
    tagWriter.writeAttribute(OptionTag.VALUE_VARIABLE_NAME, renderedValue);
    if (isSelected(value)) {
        tagWriter.writeAttribute("selected", "selected");
    }
    if (isDisabled()) {
        tagWriter.writeAttribute("disabled", "disabled");
    }
    tagWriter.appendValue(label);
    tagWriter.endTag();
  }

  private boolean isSelected(Object resolvedValue) {
    BindStatus bindStatus = getBindStatus();      
    return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null;
  }
}

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

 <form:select id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}">                         
   <c:if test="${not required}">                                
     <formExtension:nulloption value="null"></formExtension:nulloption>
   </c:if>
   <form:options items="${items}" itemValue="${fn:escapeXml(itemValue)}" itemLabel="${sec_itemLabel}"/>
</form:select>
1 голос
/ 01 сентября 2012

Есть еще один способ обойти эту проблему.Мое решение не требует изменения файла select.tagx.

Я внес изменения в двух местах.

Во-первых, на BookController я переписал метод заполнения для этого конкретного объекта,Publisher, например.

@ModelAttribute("publisher")
public Collection<Publisher> populatePublisher() {
    Collection<Publisher> result = new ArrayList<Publisher>();

    // Add an empty item
    Publishertmp = new Publisher();
    tmp.setId(0L);
    result.add(tmp);
    result.addAll(Publisher.findAllPublisher());

    return result ;
}

Важно установить Id равным 0. Если Id не 0, первый пустой элемент не будет отображаться.

Тогда яперезаписал ApplicationConversionServiceFactoryBean

protected void installFormatters(FormatterRegistry registry) {

    registry.addConverter(new PublisherConverter());

    super.installFormatters(registry);
    // Register application converters and formatters 
}

static class PublisherConverter implements Converter<Publisher, String> {
    public String convert(Publisher publisher) {
        if (publisher.getId().intValue() == 0) {
            return "-- Not Selected --";
        }
        return new StringBuilder().append(publisher.toString());
    }
}

Сменив конвертер, вы увидите -- Not Selected -- на первом элементе.Когда форма отправляется в контроллер, вам просто нужно добавить код, чтобы определить, выбран ли пустой издатель, и выполнить настройку в соответствии с вашей логикой.Я думаю, что это все.

Вы можете оставить выпадающий список издателя как required = "true", и он все равно будет работать.

...