3 Stimmen

Wie kann man eine nicht obligatorische Drop Down Box in Spring Roo/Dojo erstellen?

In Spring Roo (1.1.5) habe ich eine Entität "Book", die einen Verweis auf die Entität "Publisher" haben kann.

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

Jetzt habe ich die von Roo generierten Controller- und JSPX-Dateien. In der GUI für das Erstellen und Aktualisieren des Buches gibt es die von Roo generierte Drop Down Box (dekoriert durch dijit.form.FilteringSelect ), um den Verlag auszuwählen. Der Benutzer MUSS jedoch einen Verlag auswählen; es gibt kein "leeres" Feld!

Mein erster Versuch bestand einfach darin, eine null Wert in die Liste ein, der die Optionen für die Dropdownbox darstellt. Aber das ist fehlgeschlagen. ( java.lang.IllegalArgumentException: Bean object must not be null ) - Das könnte also der falsche Weg sein.

Bevor ich also versuche, die select.tagx Datei durch meine eigene, möchte ich fragen wenn jemand bereits dieses Problem gelöst haben (mit einem optionalen Dropdown-Box mit Spring Roo / Dojo), oder tun ich etwas völlig falsch und es sollte im Normalfall funktionieren, ohne dass etwas Neues implementiert werden muss?

3voto

Ralph Punkte 114913

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>

1voto

Martin Chan Punkte 11

Es gibt eine andere Möglichkeit, dieses Problem zu umgehen. Meine Lösung erfordert keine Änderung der select.tagx Datei.

Ich habe an zwei Stellen Änderungen vorgenommen.

Erstens, bei der BookController Ich hatte die populate-Methode für dieses spezielle Objekt überschrieben, Publisher zum Beispiel.

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

Es ist wichtig, dass die Id auf 0 gesetzt wird, da sonst der erste leere Eintrag nicht angezeigt wird.

Dann hatte ich die 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());
    }
}

Wenn Sie den Konverter ändern, können Sie sehen -- Not Selected -- auf den ersten Punkt. Wenn das Formular an den Controller übermittelt wird, müssen Sie lediglich Code hinzufügen, um zu erkennen, ob ein leerer Verlag ausgewählt wurde, und eine Anpassung an Ihre Logik vornehmen. Ich glaube, das war's schon.

Sie können das Dropdown-Feld für den Herausgeber so belassen required = "true" und es wird trotzdem funktionieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X