简体   繁体   English

如何在 Spring Roo/Dojo 中做一个非强制性的下拉框?

[英]How to do a not mandatory Drop Down Box in Spring Roo/Dojo?

In Spring Roo (1.1.5) I have an Entity "Book" that CAN have a reference to Entity "Publisher".在 Spring Roo (1.1.5) 中,我有一个实体“Book”,它可以引用实体“Publisher”。

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

Now I have the Roo generated Controller and JSPX files.现在我有了 Roo 生成的 Controller 和 JSPX 文件。 In the GUI the for creating and updating the Book there is the Roo generated Drop Down Box (decorated by dijit.form.FilteringSelect ) to select the Publisher.在用于创建和更新 Book 的 GUI 中有 Roo 生成的下拉框(由dijit.form.FilteringSelect装饰)到 select 发布者。 But the user MUST select a Publisher;但是用户必须 select 是发布者; there is no "Empty" field!没有“空”字段!

My first try was simply to add a null value to the list which represents the options for the drop downbox.我的第一次尝试只是简单地将null值添加到代表下拉框选项的列表中。 But that failed.但那失败了。 ( java.lang.IllegalArgumentException: Bean object must not be null ) -- So this may be the wrong way. java.lang.IllegalArgumentException: Bean object must not be null )——所以这可能是错误的方法。

So before I try to extend the select.tagx file by my own, I want to ask if someone have already solved that problem (having a optional drop downbox with Spring Roo/Dojo), or do I something completely wrong and it should work in normmal case with out implmenting something new?所以在我尝试自己扩展select.tagx文件之前,我想问一下是否有人已经解决了这个问题(有一个带有 Spring Roo/Dojo 的可选下拉框),或者我完全错了,它应该可以工作没有实施新事物的正常情况?

I have found a solution, but this works only because my application is not a standard Roo application any more.我找到了一个解决方案,但这只是因为我的应用程序不再是标准的 Roo 应用程序。 Anyway, I will explain my solution, maybe someone find an way to adapt it for standard Roo applications.无论如何,我会解释我的解决方案,也许有人会找到一种方法来适应标准 Roo 应用程序。

The idea is to add an empty selection in the dropdown box when the required attribute is false .这个想法是当required属性为false时,在下拉框中添加一个空选择。 The main problem is that the dijti/dojo extension will not work correct if there is one option in the dropdown box without an value .主要问题是如果下拉框中有一个选项没有value ,则 dijti/dojo 扩展将无法正常工作。 So my solution was to give them for example the value "null" ( <option value="null></option> ). On server side one must change the converter that convert the database id (that is the normal value) to an Entity (by loading it from the database) a bit, so that it converts the String "null" to null instead of an entity.所以我的解决方案是给他们例如value "null"<option value="null></option> )。在服务器端,必须更改将数据库 id(即正常值)转换为实体(通过从数据库加载)一点,以便将字符串"null"转换为null而不是实体。

But that is the problem with spring Roo.但这就是 spring Roo 的问题。 Roo uses the org.springframework.core.convert.support.IdToEntityConverter that is automatically registered (not documented https://jira.springsource.org/browse/SPR-7461 ) and will try to convert every object to an entity if the entity class as a static finder method. Roo 使用自动注册的org.springframework.core.convert.support.IdToEntityConverter (未记录https://jira.springsource.org/browse/SPR-7461 )并将尝试将每个 object 转换为实体,如果该实体class 作为 static 查找器方法。 I have found no way to modify its behaviour.我发现没有办法修改它的行为。

But I personally have a lot of luck, because some time ago I changed my application that it does not have that static finder, so I have my own generic Id to entity converter that is easy to change.但我个人有很多运气,因为前段时间我更改了我的应用程序,它没有那个 static finder,所以我有自己的通用 Id 到实体转换器,很容易更改。 The converter converts String to Entity.转换器将字符串转换为实体。 If the String is "null" it returns null, else it converts the String to a number and load the Entity by this number/id.如果字符串为“null”,则返回 null,否则将字符串转换为数字并按此数字/ID 加载实体。

For the view, it seams that one have to extend the select.tagx file.对于视图,似乎必须扩展select.tagx文件。

The select.tagx file contains 12 different ways to fill the select box. select.tagx文件包含 12 种不同的方式来填充 select 框。

  • 6 of them are for multiple select, so they can stay like they are.其中 6 个用于多个 select,因此它们可以保持原样。
  • 2 of if the not multiple ones are for disabled form binding, there one must add this block right after the select tag如果不是多个用于禁用表单绑定的 2 个,则必须在 select 标记之后添加此块

line 75, 130,第 75、130 行,

<c:if test="${not required}">
    <option value="null"></option>
</c:if>
  • the other 4 are a bit more complicated其他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}"/>

They one need to replace the complete tag by ( I will only demonstrate it for the last of that 4, but the other are similar, except one has to remove the itemVlaue and or itemLabel parameter )他们需要将完整的标签替换为(我只会在这 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>

Now it should work.现在它应该可以工作了。


But it has a small flaw.但它有一个小缺陷。 If there is a Book with no Publisher, then the empty dropdown option will not have the select attribute.如果有一本书没有出版商,那么空下拉选项将没有 select 属性。 This is not so bad, because it is the top most option and will be displayed if no other option is selected.这还不错,因为它是最顶层的选项,如果没有选择其他选项,就会显示出来。

If someone can not accept this flaw, then one way to handle this problem is to write an own jsp tag extending org.springframework.web.servlet.tags.form.Option (the class that does the spring option tag). If someone can not accept this flaw, then one way to handle this problem is to write an own jsp tag extending org.springframework.web.servlet.tags.form.Option (the class that does the spring option tag). There are only two things that one really need to change:真正需要改变的只有两件事:

1) the method isSelected(Object resolvedValue) must return true if the bind status is null (so this method becomes really easy) 1)如果绑定状态是null,方法isSelected(Object resolvedValue)必须返回true(所以这个方法变得非常简单)

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

2) if the tag is rendered without or empty body (method renderDefaultContent ) the content of the rendered html option should be empty but not the value . 2) 如果标签在没有或空主体的情况下呈现(方法renderDefaultContent ),则呈现的 html option的内容应该是空的,而不是value So the second parameter of the renderOption(SpecialWay) method must be set fix to an empty string.所以 renderOption(SpecialWay) 方法的第二个参数必须设置为空字符串。

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

But because the isSelected method is private and can not be override, one must copy the renderOption (can rename it) and must change it so that it invokes the "new" isSelected method.但是因为isSelected方法是私有的并且不能被覆盖,所以必须复制renderOption (可以重命名它)并且必须更改它,以便它调用“新的”isSelected 方法。 The same must be done to the two methods renderDefaultContent and renderFromBodyContent because renderOption is private too.必须对两个方法renderDefaultContentrenderFromBodyContent做同样的事情,因为renderOption也是私有的。

So one came up with this class:于是有人想出了这个 class:

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;
  }
}

Next thing to do is add this class to an tag lib definition so that it can be used in the select.tagx接下来要做的是将此 class 添加到标签库定义中,以便可以在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>

There is another way of getting around this problem.还有另一种解决这个问题的方法。 My solution does not require changing the select.tagx file.我的解决方案不需要更改select.tagx文件。

I made changes to two places.我对两个地方进行了更改。

Firstly, at the BookController , I had overwritten the populate method for this particular object, Publisher , for example.首先,在BookController中,我为这个特定的 object,例如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 ;
}

It is important to set the Id to 0. If the Id is not 0, the first empty item will not be displayed.将Id设置为0很重要。如果Id不为0,则不会显示第一个空项目。

Then I had overwritten the ApplicationConversionServiceFactoryBean然后我覆盖了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());
    }
}

By changing the converter, you can see -- Not Selected -- on the first item.通过更改转换器,您可以在第一项上看到-- Not Selected -- When the form is submitted to the controller, you just need to add code to identify if an empty publisher is selected and make adjustment to suit your logic.当表单提交到 controller 时,您只需添加代码以识别是否选择了空发布者并根据您的逻辑进行调整。 I believe that's about it.我相信就是这样。

You can leave the publisher drop down box as required = "true" and it will still work.您可以根据required = "true" ,它仍然可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM