Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GWT is there a <label> widget?

Tags:

I have to add a form to a small piece of AJAX functionality implemented in GWT. In HTML terms, I'd like

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

It appears the Label widget in GWT simply renders as a DIV.

Ideally I would like clicking on the label text to focus the associated input. This is built-in browser functionality I don't want to have to mess around with ClickHandlers on label divs!

Has anyone faced this issue? Does exist as a built-in widget but is called something else?

EDIT: Have come up with the following. Maybe there is a better way?

HTML label = new HTML(); label.setHTML("<label for='"+input.getElement().getId()+"'>"+labelText+"</label>"); 
like image 849
AlexJReid Avatar asked Oct 07 '09 20:10

AlexJReid


2 Answers

By popular demand, I present to you InputLabel, a <label> + <input type="text"> Widget :)

This is based on the CheckBox class (which wraps an <input type="checkbox"> element) - it hasn't been tested thoroughly - I leave that to the reader ;)

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

The answer below is left for those that prefer to use "standard" GWT Widgets and/or prefer to do it the other way :)

You can easily create a <label> element with DOM.createLabel():

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

But I'd stick with the Widgets provided by GWT - they were built and chosen by the GWT so that they will look and behave exactly the same in all supported browsers. The approach they chose (for example, if you place an Image inline, it will be wrapped inside a table, iirc - because setting it inline via display:inline will not work in all browsers :cough:IE:cough:).

tl;dr: unless you have a very specific need (like creating your own, low-level elements), stick with the provided Widgets (or create own via Composite) - you'll benefit more.

PS: And if you are worried about web standards, accessibility, etc - don't, for example, most standard GWT widgets support ARIA - something you would have to do yourself, had you built your own components.

Edit: answer to AlexJReid's comment:

You can send data via form using FormPanel (it's worth noting that this way it will work on all browsers, because, unlike other browsers, IE6 fires a different event then the other browsers; additionally, the form's target will be set to an iframe - thanks to that, the page won't have to reload - that would beat the purpose of 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); 

Note the box.setName("name"); line - this is were you set the name that will be used for the value of that TextBox when you submit this form. So, what Widgets does FormPanel support? Those that implement the com.google.gwt.user.client.ui.HasName interface:

  • TextBox
  • PasswordTextBox
  • RadioButton
  • SimpleRadioButton
  • CheckBox
  • SimpleCheckBox
  • TextArea
  • ListBox
  • FileUpload
  • Hidden

(you can add, of course, any Widget, but only the values of those above will be sent)

Last thing: unless you really have to use form (like when sending files, or something similar), the RequestBuilder might be worth trying - it's using the XmlHttpRequest behing the hood - the mother/father of AJAX ;)

like image 198
4 revs, 2 users 99% Avatar answered Nov 30 '22 17:11

4 revs, 2 users 99%


With UIbinder, you can simply create standard html labels in your template with the ui:field attribute.

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

In your View, you can reference this element using an annotation:

@UiField LabelElement myLabel; 

Note that some of the methods you're used to using with GWT widgets aren't available.

e.g. instead of:

myLabel.setVisible(false); 

you'll have to use

myLabel.setAttribute("style", "display:none"); 
like image 29
Bayard Randel Avatar answered Nov 30 '22 18:11

Bayard Randel