2009-10-07 9 views
23

Muszę dodać formularz do małego elementu funkcjonalności AJAX zaimplementowanego w GWT. Pod względem HTML, chciałbymGWT czy istnieje widżet <label>?

<label for="personName">Name:</label><input type="text" size="50" id="personName"/> 

Wydaje widget Etykieta w GWT po prostu renderuje jako DIV.

Idealnie chciałbym kliknąć tekst etykiety, aby ustawić odpowiednie wejście. Jest to wbudowana funkcjonalność przeglądarki, której nie chcę mieć problemu z ClickHandlers na etykietach divs!

Czy ktoś napotkał ten problem? Czy istnieje jako wbudowany widget, ale nazywa się coś innego?

EDYCJA: Wymyśliłem następujące. Może jest lepszy sposób?

HTML label = new HTML(); 
label.setHTML("<label for='"+input.getElement().getId()+"'>"+labelText+"</label>"); 

Odpowiedz

16

Przez popularne popytu, przedstawiam wam InputLabel, A <label> + <input type="text"> Widget :)

ta opiera się na klasie CheckBox (która owija element <input type="checkbox">) - to nie mazostały dokładnie testowane - Nie pozostawiać że do czytnika;)

import com.google.gwt.dom.client.Document; 
import com.google.gwt.dom.client.InputElement; 
import com.google.gwt.dom.client.LabelElement; 
import com.google.gwt.event.dom.client.ChangeEvent; 
import com.google.gwt.event.dom.client.ChangeHandler; 
import com.google.gwt.event.dom.client.HasChangeHandlers; 
import com.google.gwt.event.logical.shared.ValueChangeEvent; 
import com.google.gwt.event.logical.shared.ValueChangeHandler; 
import com.google.gwt.event.shared.HandlerRegistration; 
import com.google.gwt.user.client.DOM; 
import com.google.gwt.user.client.Element; 
import com.google.gwt.user.client.Event; 
import com.google.gwt.user.client.EventListener; 
import com.google.gwt.user.client.ui.ButtonBase; 
import com.google.gwt.user.client.ui.FormPanel; 
import com.google.gwt.user.client.ui.HasName; 
import com.google.gwt.user.client.ui.HasValue; 
import com.google.gwt.user.client.ui.RadioButton; 
import com.google.gwt.user.client.ui.UIObject; 
import com.google.gwt.user.client.ui.Widget; 

public class InputLabel extends ButtonBase implements HasName, HasValue<String>, HasChangeHandlers { 
    InputElement inputElem; 
    LabelElement labelElem; 
    private boolean valueChangeHandlerInitialized; 

    /** 
    * Creates an input box with no label. 
    */ 
    public InputLabel() { 
    this(DOM.createInputText()); 
    //setStyleName("gwt-CheckBox"); //TODO: add a valid style name 
    } 

    /** 
    * Creates an input box with the specified text label. 
    * 
    * @param label the check box's label 
    */ 
    public InputLabel(String label) { 
    this(); 
    setText(label); 
    } 

    /** 
    * Creates an input box with the specified text label. 
    * 
    * @param label the input box's label 
    * @param asHTML <code>true</code> to treat the specified label as html 
    */ 
    public InputLabel(String label, boolean asHTML) { 
    this(); 
    if (asHTML) { 
     setHTML(label); 
    } else { 
     setText(label); 
    } 
    } 

    protected InputLabel(Element elem) { 
    super(DOM.createSpan()); 
    inputElem = InputElement.as(elem); 
    labelElem = Document.get().createLabelElement(); 

    getElement().appendChild(labelElem); 
    getElement().appendChild(inputElem); 

    String uid = DOM.createUniqueId(); 
    inputElem.setPropertyString("id", uid); 
    labelElem.setHtmlFor(uid); 

    // Accessibility: setting tab index to be 0 by default, ensuring element 
    // appears in tab sequence. FocusWidget's setElement method already 
    // calls setTabIndex, which is overridden below. However, at the time 
    // that this call is made, inputElem has not been created. So, we have 
    // to call setTabIndex again, once inputElem has been created. 
    setTabIndex(0); 
    } 

    public HandlerRegistration addValueChangeHandler(
     ValueChangeHandler<String> handler) { 
    // Is this the first value change handler? If so, time to add handlers 
    if (!valueChangeHandlerInitialized) { 
     addChangeHandler(new ChangeHandler() { 
     public void onChange(ChangeEvent event) { 
      ValueChangeEvent.fire(InputLabel.this, getValue()); 
     } 
     }); 
     valueChangeHandlerInitialized = true; 
    } 
    return addHandler(handler, ValueChangeEvent.getType()); 
    } 

    /** 
    * Returns the value property of the input element that backs this widget. 
    * This is the value that will be associated with the InputLabel name and 
    * submitted to the server if a {@link FormPanel} that holds it is submitted. 
    * <p> 
    * This will probably return the same thing as {@link #getValue}, left here for magic reasons. 
    */ 
    public String getFormValue() { 
    return inputElem.getValue(); 
    } 

    @Override 
    public String getHTML() { 
    return labelElem.getInnerHTML(); 
    } 

    public String getName() { 
    return inputElem.getName(); 
    } 

    @Override 
    public int getTabIndex() { 
    return inputElem.getTabIndex(); 
    } 

    @Override 
    public String getText() { 
    return labelElem.getInnerText(); 
    } 

    /** 
    * Gets the text value of the input element. 
    * <p> 
    * @return the value of the input box. 
    * Will not return null 
    */ 
    public String getValue() { 
    if (isAttached()) { 
     return inputElem.getValue(); 
    } else { 
     return inputElem.getDefaultValue(); 
    } 
    } 

    @Override 
    public boolean isEnabled() { 
    return !inputElem.isDisabled(); 
    } 

    @Override 
    public void setAccessKey(char key) { 
    inputElem.setAccessKey("" + key); 
    } 

    @Override 
    public void setEnabled(boolean enabled) { 
    inputElem.setDisabled(!enabled); 
    if (enabled) { 
     removeStyleDependentName("disabled"); 
    } else { 
     addStyleDependentName("disabled"); 
    } 
    } 

    @Override 
    public void setFocus(boolean focused) { 
    if (focused) { 
     inputElem.focus(); 
    } else { 
     inputElem.blur(); 
    } 
    } 

    /** 
    * Set the value property on the input element that backs this widget. This is 
    * the value that will be associated with the InputLabel's name and submitted to 
    * the server if a {@link FormPanel} that holds it is submitted. 
    * <p> 
    * Don't confuse this with {@link #setValue}. 
    * 
    * @param value 
    */ 
    public void setFormValue(String value) { 
    inputElem.setAttribute("value", value); 
    } 

    @Override 
    public void setHTML(String html) { 
    labelElem.setInnerHTML(html); 
    } 

    public void setName(String name) { 
    inputElem.setName(name); 
    } 

    @Override 
    public void setTabIndex(int index) { 
    // Need to guard against call to setTabIndex before inputElem is 
    // initialized. This happens because FocusWidget's (a superclass of 
    // InputLabel) setElement method calls setTabIndex before inputElem is 
    // initialized. See InputLabel's protected constructor for more information. 
    if (inputElem != null) { 
     inputElem.setTabIndex(index); 
    } 
    } 

    @Override 
    public void setText(String text) { 
    labelElem.setInnerText(text); 
    } 

    /** 
    * Sets the text in the input box. 
    * <p> 
    * Note that this <em>does not</em> set the value property of the 
    * input element wrapped by this widget. For access to that property, see 
    * {@link #setFormValue(String)} 
    * 
    * @param value the text to set; must not be null 
    * @throws IllegalArgumentException if value is null 
    */ 
    public void setValue(String value) { 
    setValue(value, false); 
    } 

    /** 
    * Sets the text in the input box, firing {@link ValueChangeEvent} if 
    * appropriate. 
    * <p> 
    * Note that this <em>does not</em> set the value property of the 
    * input element wrapped by this widget. For access to that property, see 
    * {@link #setFormValue(String)} 
    * 
    * @param value true the text to set; must not be null 
    * @param fireEvents If true, and value has changed, fire a 
    *   {@link ValueChangeEvent} 
    * @throws IllegalArgumentException if value is null 
    */ 
    public void setValue(String value, boolean fireEvents) { 
    if (value == null) { 
     throw new IllegalArgumentException("value must not be null"); 
    } 

    String oldValue = getValue(); 
    inputElem.setValue(value); 
    inputElem.setDefaultValue(value); 
    if (value.equals(oldValue)) { 
     return; 
    } 
    if (fireEvents) { 
     ValueChangeEvent.fire(this, value); 
    } 
    } 

    // Unlike other widgets the InputLabel sinks on its inputElement, not 
    // its wrapper 
    @Override 
    public void sinkEvents(int eventBitsToAdd) { 
    if (isOrWasAttached()) { 
     Event.sinkEvents(inputElem, 
      eventBitsToAdd | Event.getEventsSunk(inputElem)); 
    } else { 
     super.sinkEvents(eventBitsToAdd); 
    } 
    } 


    /** 
    * <b>Affected Elements:</b> 
    * <ul> 
    * <li>-label = label next to the input box.</li> 
    * </ul> 
    * 
    * @see UIObject#onEnsureDebugId(String) 
    */ 
    @Override 
    protected void onEnsureDebugId(String baseID) { 
    super.onEnsureDebugId(baseID); 
    ensureDebugId(labelElem, baseID, "label"); 
    ensureDebugId(inputElem, baseID, "input"); 
    labelElem.setHtmlFor(inputElem.getId()); 
    } 

    /** 
    * This method is called when a widget is attached to the browser's document. 
    * onAttach needs special handling for the InputLabel case. Must still call 
    * {@link Widget#onAttach()} to preserve the <code>onAttach</code> contract. 
    */ 
    @Override 
    protected void onLoad() { 
    setEventListener(inputElem, this); 
    } 

    /** 
    * This method is called when a widget is detached from the browser's 
    * document. Overridden because of IE bug that throws away checked state and 
    * in order to clear the event listener off of the <code>inputElem</code>. 
    */ 
    @Override 
    protected void onUnload() { 
    // Clear out the inputElem's event listener (breaking the circular 
    // reference between it and the widget). 
    setEventListener(asOld(inputElem), null); 
    setValue(getValue()); 
    } 

    /** 
    * Replace the current input element with a new one. Preserves 
    * all state except for the name property, for nasty reasons 
    * related to radio button grouping. (See implementation of 
    * {@link RadioButton#setName}.) 
    * 
    * @param elem the new input element 
    */ 
    protected void replaceInputElement(Element elem) { 
    InputElement newInputElem = InputElement.as(elem); 
    // Collect information we need to set 
    int tabIndex = getTabIndex(); 
    String checked = getValue(); 
    boolean enabled = isEnabled(); 
    String formValue = getFormValue(); 
    String uid = inputElem.getId(); 
    String accessKey = inputElem.getAccessKey(); 
    int sunkEvents = Event.getEventsSunk(inputElem); 

    // Clear out the old input element 
    setEventListener(asOld(inputElem), null); 

    getElement().replaceChild(newInputElem, inputElem); 

    // Sink events on the new element 
    Event.sinkEvents(elem, Event.getEventsSunk(inputElem)); 
    Event.sinkEvents(inputElem, 0); 
    inputElem = newInputElem; 

    // Setup the new element 
    Event.sinkEvents(inputElem, sunkEvents); 
    inputElem.setId(uid); 
    if (!accessKey.equals("")) { 
     inputElem.setAccessKey(accessKey); 
    } 
    setTabIndex(tabIndex); 
    setValue(checked); 
    setEnabled(enabled); 
    setFormValue(formValue); 

    // Set the event listener 
    if (isAttached()) { 
     setEventListener(asOld(inputElem), this); 
    } 
    } 

    private Element asOld(com.google.gwt.dom.client.Element elem) { 
    Element oldSchool = elem.cast(); 
    return oldSchool; 
    } 

    private void setEventListener(com.google.gwt.dom.client.Element e, 
     EventListener listener) { 
    DOM.setEventListener(asOld(e), listener); 
    } 

    @Override 
    public HandlerRegistration addChangeHandler(ChangeHandler handler) { 
     return addDomHandler(handler, ChangeEvent.getType()); 
    } 
} 

Odpowiedź poniżej pozostało dla tych, którzy wolą korzystać z „standard” GWT widżety i/lub wolą zrobić to w inny sposób :)

można łatwo tworzyć element <label> z DOM.createLabel():

LabelElement label = DOM.createLabel().cast(); 
label.setHtmlFor("inputId"); 

Ale pozostaję z widżetami dostarczonymi przez GWT - zostały one zbudowane i wybrane przez GWT, aby wyglądały i zachowywały się dokładnie tak samo we wszystkich obsługiwanych przeglądarkach. Podejście, które wybrali (na przykład, jeśli wstawisz inline Image, zostanie on zawinięty wewnątrz tabeli, iirc - ponieważ ustawienie go w linii przez display:inline nie będzie działać we wszystkich przeglądarkach: kaszel: IE: kaszel :).

tl; dr: chyba że masz bardzo konkretne potrzeby (np. Tworzenie własnych, niskopoziomowych elementów), trzymaj się dostarczonego Widgets (lub stwórz własne przez Composite) - zyskasz więcej.

PS: A jeśli martwisz się standardami sieciowymi, ułatwieniami dostępu itp. - nie na przykład większość standardowych widżetów GWT support ARIA - coś, co musiałbyś zrobić sam, gdybyś zbudował własne komponenty.

Edycja: Odpowiedź na komentarz AlexJReid za:

można wysyłać dane poprzez formularz używając FormPanel (warto zauważyć, że w ten sposób będzie działać na wszystkich przeglądarkach, ponieważ, w przeciwieństwie do innych przeglądarek, IE6 odpala inną imprezę wówczas inne przeglądarki; dodatkowo, forma na target zostanie ustawiony na iframe - dzięki, że strona nie będzie musiał przeładować - które pokonać cel AJAX :)):

final FormPanel form = new FormPanel(); 
form.setAction("page.php"); 

TextBox box = new TextBox(); 
box.setName("name"); 
box.setText("fasdf"); 

Button button = new Button("Send", new ClickHandler() { 
    @Override 
    public void onClick(ClickEvent event) { 
     form.submit(); 
    } 
}); 

form.add(box); 
form.add(button); 

Uwaga na box.setName("name"); linia - to byłeś ty ustaw nazwę, która będzie używana dla wartości tego TextBox po przesłaniu tego formularza. Więc, co Widgets obsługuje FormPanel? Te, które implementują interfejs com.google.gwt.user.client.ui.HasName:

  • pole tekstowe
  • PasswordTextBox
  • RadioButton
  • SimpleRadioButton
  • CheckBox
  • SimpleCheckBox
  • TextArea
  • ListBox
  • FileUpload
  • Ukryte

(można dodać, oczywiście, dowolny widget, ale tylko wartości tych powyżej zostaną wysłane)

Ostatnia rzecz: chyba że naprawdę trzeba użyć formularza (jak podczas wysyłania plików lub coś podobnego), to RequestBuilder może być warto spróbować - to używając XmlHttpRequest behing kaptur - matka/ojciec AJAX;)

+0

Kłucie tylko widgetów warunkiem przez GWT, jak byś postąpił w tworzeniu formularza składającego się z etykiet i danych wejściowych? – AlexJReid

+1

Myślę, że przegapiłeś punkt pytania. Wydawało się, że ma on działającą formę, chciał tylko, aby etykieta tekstowa obok TextBox była

+2

Wow, nigdy tak naprawdę nie zrozumiałem cię może to zrobić :) (potem znowu, nie widzę powodu, dla którego kliknęłoby się etykietę, zamiast pola tekstowego). Najczystszym sposobem na zrobienie tego w GWT jest prawdopodobnie stworzenie Composite z etykietą i TextBox, dodanie ClickHandlera do Label i voila;) –

9

z UIbinder, można po prostu stworzyć standardowe etykiety html w szablonie z w polu Ui: Atrybut.

<label ui:field="myLabel">Some Text</label> 

Pana zdaniem, można odwoływać się do tego elementu za pomocą adnotacji:

@UiField LabelElement myLabel; 

pamiętać, że niektóre z metod jesteś przyzwyczajony do korzystania z widżetów GWT nie są dostępne.

np.zamiast:

myLabel.setVisible(false); 

będziesz musiał użyć

myLabel.setAttribute("style", "display:none"); 
7

Jako alternatywę dla metody Bayard za stworzenie elementu @UiField dla każdej etykiety, można również wykonać następujące czynności na etykiecie i tekstowym kombinacja:

<label for="{myTextBox.getElement.getId}">Some field:</label> 
<g:TextBox ui:field="myTextBox"/> 

nie wolno ustawić ui: właściwości pól i id tego samego elementu, więc trzeba dać pola tekstowego identyfikatora w kodzie java:

... 

@UiField(provided=true) TextBox myTextBox = new TextBox(); 

public MyFormView() { 
    myTextBox.getElement().setId(DOM.createUniqueId()); 
    uiBinder.createAndBindUi(this); 
} 

... 

Oto Ray Ryan's comment on UiBinder id generation, który wskazał mi właściwy kierunek. Wspomina o generowaniu identyfikatora środowiska wykonawczego, które wyeliminowałoby potrzebę fragmentu kodu java, ale o ile mogę powiedzieć, że został złomowany.

+0

Teraz jest to bardzo przydatne, ponieważ wymaga minimalnego wysiłku. – migu

0

miałem te same potrzeby, co skończyło się tworząc własny widget

public class Label extends Widget implements HasText { 

public Label() { 
    setElement(DOM.createLabel()); 
} 

@Override 
public void add(Widget w) { 
    super.add(w, getElement()); 
} 

@Override 
public String getText() { 
    return getElement().getInnerText(); 
} 

@Override 
public void setText(String text) { 
    getElement().setInnerText((text == null) ? "" : text); 
} 

public void setFor(String forWho) { 
    getElement().setAttribute("for", forWho); 
} 

}

0

Napisałem prosty widget do stosowania w UiBinder nazywa InputLabel (kod źródłowy poniżej). Używa etykiety HTML pod maską, ale ui: pola do odwoływania się do widgetów wejściowych. Najlepsze z obu światów, mam nadzieję.

Widget InputLabel może być stosowany w UiBinder tak:

xmlns:tmp='urn:import:{REPLACE_WITH_JAVA_PACKAGE_PATH_TO_INPUTLABEL}' 

    <tmp:InputLabel for="{fieldRef}">Label text</tmp:InputLabel> 
    <g:FlowPanel ui:field="fieldRef"> 
     <g:TextBox/> 
    </g:FlowPanel> 

lub tak:

<g:FlowPanel> 
     <g:CheckBox ui:field="fieldRef" /> 
    </g:FlowPanel> 
    <tmp:InputLabel for="{fieldRef}">Label text</tmp:InputLabel> 

Kod źródłowy Java jest:

import com.google.gwt.dom.client.*; 
import com.google.gwt.i18n.client.HasDirection; 
import com.google.gwt.i18n.shared.DirectionEstimator; 
import com.google.gwt.i18n.shared.HasDirectionEstimator; 
import com.google.gwt.user.client.DOM; 
import com.google.gwt.user.client.ui.DirectionalTextHelper; 
import com.google.gwt.user.client.ui.HasDirectionalText; 
import com.google.gwt.user.client.ui.IsWidget; 
import com.google.gwt.user.client.ui.Widget; 

public class InputLabel extends Widget implements HasDirectionalText, HasDirectionEstimator { 

    final DirectionalTextHelper directionalTextHelper; 
    private boolean init = false; 

    public InputLabel() { 
     this(Document.get().createLabelElement()); 
    } 

    public InputLabel(Element element) { 
     assert (LabelElement.TAG.equalsIgnoreCase(element.getTagName())); 

     this.setElement(element); 
     this.directionalTextHelper = new DirectionalTextHelper(this.getElement(), true); 
    } 

    public DirectionEstimator getDirectionEstimator() { 
     return this.directionalTextHelper.getDirectionEstimator(); 
    } 

    public void setDirectionEstimator(DirectionEstimator directionEstimator) { 
     this.directionalTextHelper.setDirectionEstimator(directionEstimator); 
    } 

    public void setDirectionEstimator(boolean enabled) { 
     this.directionalTextHelper.setDirectionEstimator(enabled); 
    } 

    private InputElement getInputElement(Widget widget) { 
     if (widget.getElement().hasTagName(InputElement.TAG)) return InputElement.as(widget.getElement()); 
     NodeList<Element> l = widget.getElement().getElementsByTagName(InputElement.TAG); 
     if (l.getLength() > 0) { 
      return InputElement.as(l.getItem(0)); 
     } 

     return null; 
    } 

    public void setFor(IsWidget target) { 
     if (init) return; 
     init = true; 
     // 

     final InputElement input = getInputElement(target.asWidget()); 
     if (input != null) { 
      if (!input.hasAttribute("id")) input.setId(DOM.createUniqueId()); 
      getElement().setAttribute("for", input.getId()); 
     } 
    } 

    public void setForm(String form) { 
     getElement().setAttribute("form", form); 
    } 

    public String getText() { 
     return this.directionalTextHelper.getTextOrHtml(false); 
    } 

    public void setText(String text) { 
     this.directionalTextHelper.setTextOrHtml(text, false); 
    } 

    public HasDirection.Direction getTextDirection() { 
     return this.directionalTextHelper.getTextDirection(); 
    } 

    public void setText(String text, HasDirection.Direction dir) { 
     this.directionalTextHelper.setTextOrHtml(text, dir, false); 
    } 
}