Ich habe eine Lösung gefunden, die aber nur funktioniert, weil meine Anwendung keine Standard-Roo-Anwendung mehr ist. Wie auch immer, ich werde meine Lösung erklären, vielleicht findet jemand einen Weg, um es für Standard-Roo-Anwendungen anzupassen.
Die Idee ist, eine leere Auswahl in die Dropdown-Box einzufügen, wenn die required
Attribut ist false
. Das Hauptproblem ist, dass die dijti/dojo-Erweiterung nicht richtig funktioniert, wenn es eine Option in der Dropdown-Box ohne ein value
. Meine Lösung war also, ihnen zum Beispiel die value
"null"
( <option value="null></option>
). Auf der Serverseite muss man den Konverter, der die Datenbank-ID (das ist der normale Wert) in ein Entity (durch Laden aus der Datenbank) umwandelt, ein wenig ändern, so dass er den String konvertiert "null"
a null
anstelle einer Entität.
Aber das ist das Problem mit Spring Roo. Roo verwendet die org.springframework.core.convert.support.IdToEntityConverter
die automatisch registriert wird (nicht dokumentiert https://jira.springsource.org/browse/SPR-7461 ) und versucht, jedes Objekt in eine Entität umzuwandeln, wenn die Entitätsklasse eine statische Finder-Methode ist. Ich habe keine Möglichkeit gefunden, dieses Verhalten zu ändern.
Aber ich persönlich habe viel Glück, denn vor einiger Zeit habe ich meine Anwendung geändert, dass es nicht, dass statische Finder, so habe ich meine eigenen generischen Id zu Entity-Konverter, die leicht zu ändern ist. Der Konverter konvertiert String in Entity. Wenn der String "null" ist, gibt er null zurück, andernfalls konvertiert er den String in eine Zahl und lädt die Entity mit dieser Zahl/Id.
Für die Ansicht scheint es, dass man die select.tagx
Datei.
El select.tagx
Datei enthält 12 verschiedene Möglichkeiten, das Auswahlfeld zu füllen.
- 6 von ihnen sind für Mehrfachauswahlen vorgesehen, so dass sie so bleiben können, wie sie sind.
- 2, wenn die nicht mehrfachen für deaktivierte Formularbindung sind, muss man diesen Block direkt nach dem select-Tag hinzufügen
Zeile 75, 130,
<c:if test="${not required}">
<option value="null"></option>
</c:if>
- die anderen 4 sind ein bisschen komplizierter
...
<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}"/>
Sie müssen den kompletten Tag durch ( Ich werde es nur für die letzte der 4 demonstrieren, aber die anderen sind ähnlich, außer dass man den Parameter itemVlaue und oder itemLabel entfernen muss )
<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>
Jetzt sollte es funktionieren.
Aber es hat einen kleinen Schönheitsfehler. Wenn es ein Buch ohne Verleger gibt, hat die leere Dropdown-Option nicht das Attribut select. Das ist nicht so schlimm, denn es ist die oberste Option und wird angezeigt, wenn keine andere Option ausgewählt ist.
Wenn jemand diesen Fehler nicht akzeptieren kann, dann ist eine Möglichkeit, dieses Problem zu behandeln, ein eigenes jsp-Tag zu schreiben, das die org.springframework.web.servlet.tags.form.Option
(die Klasse, die den Spring Option Tag ausführt). Es gibt nur zwei Dinge, die man wirklich ändern müssen:
1) die Methode isSelected(Object resolvedValue)
muss true zurückgeben, wenn der Bindungsstatus null ist (so wird diese Methode wirklich einfach)
private boolean isSelected(Object resolvedValue) {
BindStatus bindStatus = getBindStatus();
return bindStatus == null || bindStatus.getValue() == null || bindStatus.getActualValue() == null;
}
2) wenn der Tag ohne oder mit leerem Körper dargestellt wird (Methode renderDefaultContent
) den Inhalt der gerenderten HTML-Datei option
sollte leer sein, nicht aber die value
. Daher muss der zweite Parameter der Methode renderOption(SpecialWay) fest auf eine leere Zeichenkette gesetzt werden.
@Override
protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
Object value = this.pageContext.getAttribute(VALUE_VARIABLE_NAME);
renderOptionSpecialWay(value, "", tagWriter);
}
Aber weil die isSelected
Methode privat ist und nicht überschrieben werden kann, muss man die renderOption
(kann sie umbenennen) und muss sie so ändern, dass sie die "neue" isSelected-Methode aufruft. Dasselbe muss mit den beiden Methoden geschehen renderDefaultContent
y renderFromBodyContent
denn renderOption
ist auch privat.
So kam man auf diese Klasse:
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;
}
}
Als Nächstes müssen Sie diese Klasse zu einer Tag-Lib-Definition hinzufügen, damit sie in der 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>