【问题标题】:GWT is there a <label> widget?GWT 有 <label> 小部件吗?
【发布时间】:2015-09-30 23:20:35
【问题描述】:

我必须在 GWT 中实现的一小部分 AJAX 功能中添加一个表单。在 HTML 术语中,我想要

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

GWT 中的 Label 小部件似乎只是呈现为 DIV。

理想情况下,我希望单击标签文本以关注相关输入。这是内置的浏览器功能,我不想在标签 div 上使用 ClickHandlers!

有人遇到过这个问题吗?是否作为内置小部件存在但被称为其他东西?

编辑:想出了以下内容。也许有更好的方法?

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

【问题讨论】:

    标签: gwt


    【解决方案1】:

    根据大众的需求,我向您介绍 InputLabel,一个 &lt;label&gt; + &lt;input type="text"&gt; Widget :)

    这是基于 CheckBox 类(它包装了一个 &lt;input type="checkbox"&gt; 元素) - 它尚未经过彻底测试 - 我把它留给读者;)

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

    下面的答案留给那些喜欢使用“标准”GWT 小部件和/或喜欢用其他方式的人:)

    您可以使用DOM.createLabel() 轻松创建&lt;label&gt; 元素:

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

    但我会坚持使用 GWT 提供的小部件 - 它们是由 GWT 构建和选择的,因此它们在所有受支持的浏览器中的外观和行为都完全相同。他们选择的方法(例如,如果您将Image 内联,它将被包裹在一个表格iirc 中 - 因为通过display:inline 将其设置为内联将不适用于所有浏览器:cough:IE:cough:)。

    tl;dr:除非您有非常特殊的需求(例如创建自己的低级元素),否则请坚持使用提供的 Widgets(或通过 Composite 创建自己的)-您将受益更多。

    PS:如果您担心 Web 标准、可访问性等 - 例如,不要担心大多数标准 GWT 小部件support ARIA - 如果您构建了自己的组件,您将不得不自己做这些事情。

    编辑:回答 AlexJReid 的评论:

    您可以使用FormPanel 通过表单发送数据(值得注意的是,这种方式适用于所有浏览器,因为与其他浏览器不同,IE6 会触发与其他浏览器不同的事件;此外,表单的target将设置为 iframe - 多亏了这一点,页面不必重新加载 - 这将超出 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);
    

    注意box.setName("name"); 行 - 这是您在提交此表单时设置将用于该TextBox 值的名称。那么,FormPanel 支持哪些 Widgets?实现com.google.gwt.user.client.ui.HasName接口的那些:

    • 文本框
    • 密码文本框
    • 单选按钮
    • SimpleRadioButton
    • 复选框
    • SimpleCheckBox
    • 文本区域
    • 列表框
    • 文件上传
    • 隐藏

    (当然,您可以添加任何 Widget,但只会发送上述那些的值)

    最后一件事:除非你真的必须使用表单(比如发送文件时,或类似的东西),RequestBuilder 可能值得尝试 - 它使用 XmlHttpRequest 作为引擎盖 - AJAX 的母亲/父亲; )

    【讨论】:

    • 只坚持 GWT 提供的小部件,您将如何创建包含标签和输入的表单?
    • 我认为您错过了问题的重点。他似乎有一个工作表单,他只是希望 TextBox 旁边的文本标签是一个
    • 哇,我从来没有真正意识到你可以做到这一点:)(再说一次,我不明白为什么人们会点击标签,而不是文本字段)。在 GWT 中执行此操作的“最干净”的方法可能是创建一个包含标签和文本框的组合,将 ClickHandler 添加到标签中,瞧 ;)
    • 正如史蒂夫所说,我熟悉构建表单 - 我只是想使用
    • InputLabel 示例存在一些问题:它基于 CheckBox,这意味着使用将其设置为“文本”输入意味着完全不同的实现,一个基于 TextBoxBase 的实现。最好创建一个扩展组合的 Widget,您可以在其中设置输入小部件(例如 setInputWidget 或直接在构造函数中)并在 DOM 中的该小部件之前内部放置一个标签标签。这意味着您可以剥离几乎所有代码并使用标准 GWT 输入小部件。
    【解决方案2】:

    使用UIbinder,您可以使用 ui:field 属性在模板中简单地创建标准 html 标签。

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

    在您的视图中,您可以使用注释来引用此元素:

    @UiField LabelElement myLabel;
    

    请注意,您用于 GWT 小部件的某些方法不可用。

    例如而不是:

    myLabel.setVisible(false);
    

    你必须使用

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

    【讨论】:

      【解决方案3】:

      作为 Bayard 为每个标签创建 @UiField 元素的方法的替代方法,您还可以对标签和文本框组合执行以下操作:

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

      你不能在同一个元素上设置 ui:field 和 id 属性,所以你必须在 java 代码中给 TextBox 一个 id:

      ...
      
      @UiField(provided=true) TextBox myTextBox = new TextBox();
      
      public MyFormView() {
          myTextBox.getElement().setId(DOM.createUniqueId());
          uiBinder.createAndBindUi(this);
      }
      
      ...
      

      这是Ray Ryan's comment on UiBinder id generation,它为我指明了正确的方向。他确实提到了运行时 id 生成,这将消除对 java sn-p 的需求,但据我所知,这已被废弃。

      【讨论】:

      • 现在这真的很有用,因为它需要最少的努力。
      【解决方案4】:

      我也有同样的需求,最终创建了自己的小部件

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

      }

      【讨论】:

        【解决方案5】:

        我编写了一个简单的 Widget 用于 UiBinder,名为 InputLabel(源代码如下)。 它在底层使用 HTML 标签,但 ui:fields 用于引用输入小部件。我希望两全其美。

        InputLabel 小部件可以像这样在 UiBinder 中使用:

        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>
        

        或者像这样:

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

        Java源代码为:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-15
          • 2011-09-10
          • 2011-01-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多