【问题标题】:Tags input component with support for space in tagname标记输入组件,支持标记名中的空格
【发布时间】:2015-01-29 08:29:25
【问题描述】:

我需要一个输入字段,该字段能够在标记名中使用带有空格的标记,如下所示:

我知道<p:autoComplete multiple="true">支持带标签,但它使用空格来分隔标签:

我想在标记名中包含空格并使用 ;Enter 前进到下一个标记。如何创建新的自定义组件,或为此自定义 <p:autoComplete>?我正在使用 JSF 2.2 和 PrimeFaces 3.2。

【问题讨论】:

  • @BalusC 当我输入像';'这样的分隔符时或者我按回车。我开始输入以下值。
  • @BalusC 我需要一个inputText 来介绍电子邮件地址(例如 OUTLOOK、Gmail、.....)
  • @BalusC 新建自定义组件或自定义<p:autoComplete>解决我的问题的解决方案。
  • @BalusC 我使用 primefaces 3.2,
  • 正如我所见,在 PrimeFaces 5.0 中,您可以在标签中包含空格,提交标签的是输入键。所以我认为这正是您想要在这里实现的。我不确定在 PF 3.2 中是否有所不同。

标签: jsf-2 primefaces tags jsf-2.2 custom-component


【解决方案1】:

为什么不重写/修改它的 JavaScript 函数(在 autocomplete.js 中)?

感兴趣的代码和功能:

bindStaticEvents

bindStaticEvents: function() {
        var $this = this;

        this.bindKeyEvents();

        this.dropdown.mouseover(function() {
            $(this).addClass('ui-state-hover');
        }).mouseout(function() {
            $(this).removeClass('ui-state-hover');
        }).mousedown(function() {
            if($this.active) {
                $(this).addClass('ui-state-active');
            }
        }).mouseup(function() {
            if($this.active) {
                $(this).removeClass('ui-state-active');

                $this.search('');
                $this.input.focus();
            }
        }).focus(function() {
            $(this).addClass('ui-state-focus');
        }).blur(function() {
            $(this).removeClass('ui-state-focus');
        }).keydown(function(e) {
            var keyCode = $.ui.keyCode,
            key = e.which;

            if(key === keyCode.SPACE || key === keyCode.ENTER || key === keyCode.NUMPAD_ENTER) {
                $(this).addClass('ui-state-active');
            }
        }).keyup(function(e) {
            var keyCode = $.ui.keyCode,
            key = e.which;

            if(key === keyCode.SPACE || key === keyCode.ENTER || key === keyCode.NUMPAD_ENTER) {
                $(this).removeClass('ui-state-active');
                $this.search('');
                $this.input.focus();
                e.preventDefault();
                e.stopPropagation();
            }
        });

bindKeyEvents

bindKeyEvents: function() {
        var $this = this;

        this.currentText = this.input.val();
        this.previousText = this.input.val();

        //bind keyup handler
        this.input.keyup(function(e) {
            var keyCode = $.ui.keyCode,
            key = e.which,
            shouldSearch = true;

            $this.previousText = $this.currentText;
            $this.currentText = this.value;

            // Cancel a possible long running search when selecting an entry via enter
            if (key === keyCode.ENTER || key === keyCode.NUMPAD_ENTER) {
                if ($this.timeout) {
                    clearTimeout($this.timeout);
                }
                shouldSearch = false;
            }
            else if (key === keyCode.ESCAPE) {
                $this.hide();
                shouldSearch = false;
            }
            else if ((e.ctrlKey && key === 65) // ctrl+a
                || (e.ctrlKey && key === 67) // ctrl+c
                || key === keyCode.LEFT
                || key === keyCode.RIGHT
                || key === keyCode.TAB
                || key === 16 // keyCode.SHIFT
                || key === keyCode.HOME
                || key === keyCode.END
                || key === 18 // keyCode.ALT
                || key === 17 // keyCode.CONTROL
                || (key >= 112 && key <= 123)) { // F1-F12
                shouldSearch = false;
            }
            else if(key === keyCode.UP || key === keyCode.DOWN) {
                if($this.panel.is(':visible')) {
                    var highlightedItem = $this.items.filter('.ui-state-highlight');
                    if(highlightedItem.length) {
                        $this.displayAriaStatus(highlightedItem.data('item-label'));
                    }
                }

                shouldSearch = false;
            }
            else if($this.cfg.pojo && !$this.cfg.multiple && ($this.previousText !== $this.currentText)) {
                $this.hinput.val($(this).val());
            }

            if(shouldSearch) {
                var value = $this.input.val();

                if(!value.length) {
                    $this.hide();
                }

                if(value.length >= $this.cfg.minLength) {

                    //Cancel the search request if user types within the timeout
                    if($this.timeout) {
                        clearTimeout($this.timeout);
                    }

                    var delay = $this.cfg.delay;

                    if (value != '' && (key == keyCode.BACKSPACE || key == keyCode.DELETE)) {
                        delay = $this.cfg.deletionDelay;
                    }

                    $this.timeout = setTimeout(function() {
                        $this.search(value);
                    }, delay);
                }
            }

        }).keydown(function(e) {
            var keyCode = $.ui.keyCode;

            if($this.panel.is(':visible')) {
                var highlightedItem = $this.items.filter('.ui-state-highlight');

                switch(e.which) {
                    case keyCode.UP:
                        var prev = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.prevAll('.ui-autocomplete-item:first');

                        if(prev.length == 1) {
                            highlightedItem.removeClass('ui-state-highlight');
                            prev.addClass('ui-state-highlight');

                            if($this.cfg.scrollHeight) {
                                PrimeFaces.scrollInView($this.panel, prev);
                            }

                            if($this.cfg.itemtip) {
                                $this.showItemtip(prev);
                            }
                        }

                        e.preventDefault();
                        break;

                    case keyCode.DOWN:
                        var next = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.nextAll('.ui-autocomplete-item:first');

                        if(next.length == 1) {
                            highlightedItem.removeClass('ui-state-highlight');
                            next.addClass('ui-state-highlight');

                            if($this.cfg.scrollHeight) {
                                PrimeFaces.scrollInView($this.panel, next);
                            }

                            if($this.cfg.itemtip) {
                                $this.showItemtip(next);
                            }
                        }

                        e.preventDefault();
                        break;

                    case keyCode.ENTER:
                    case keyCode.NUMPAD_ENTER:
                        highlightedItem.click();

                        e.preventDefault();
                        e.stopPropagation();
                        break;

                    case 18: //keyCode.ALT:
                    case 224:
                        break;

                    case keyCode.TAB:
                        highlightedItem.trigger('click');
                        $this.hide();
                        break;
                }
            }
            else if (e.which == keyCode.TAB) {
                // clear pending search before leaving the field
                if ($this.timeout) {
                    clearTimeout($this.timeout);
                }
            }
        });
    },

bindDynamicEvents

 bindDynamicEvents: function() {
        var $this = this;

        //visuals and click handler for items
        this.items.bind('mouseover', function() {
            var item = $(this);
        if(!item.hasClass('ui-state-highlight')) {
            $this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
            item.addClass('ui-state-highlight');

            if($this.cfg.itemtip) {
                $this.showItemtip(item);
            }
        }
    })
    .bind('click', function(event) {
        var item = $(this),
        itemValue = item.attr('data-item-value');

        if($this.cfg.multiple) {
            var itemDisplayMarkup = '<li data-token-value="' + item.attr('data-item-value') + '"class="ui-autocomplete-token ui-state-active ui-corner-all ui-helper-hidden">';
            itemDisplayMarkup += '<span class="ui-autocomplete-token-icon ui-icon ui-icon-close" />';
            itemDisplayMarkup += '<span class="ui-autocomplete-token-label">' + item.attr('data-item-label') + '</span></li>';

            $this.inputContainer.before(itemDisplayMarkup);
            $this.multiItemContainer.children('.ui-helper-hidden').fadeIn();
            $this.input.val('').focus();

            $this.hinput.append('<option value="' + itemValue + '" selected="selected"></option>');
        }
        else {
            $this.input.val(item.attr('data-item-label')).focus();

            this.currentText = $this.input.val();
            this.previousText = $this.input.val();

            if($this.cfg.pojo) {
                $this.hinput.val(itemValue);
            }
        }

        $this.invokeItemSelectBehavior(event, itemValue);

        $this.hide();
    });
},

建议:将key === keyCode.SPACE 替换为key === 188(这是; 的代码)

注意:它可能会产生一些副作用,尤其是当您在其他地方使用自动完成功能时。

【讨论】:

    【解决方案2】:

    看看这个 jquery plugin,它允许围绕允许空格的标签进行自定义(参见 example page)。

    您需要创建一个带有 h:input 的复合组件,然后将 jquery 插件应用到它。这将为您提供类似于 stackoverflow 中的语言标签的内容。

    复合组件将聚合 tagit 使用 javascript 生成的隐藏输入,并设置 h:inputHidden 组件的值。像这样的:

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    
    <cc:interface shortDescription="TagIt wrapper">
        <cc:attribute name="val" type="java.lang.String" required="true" shortDescription="value" />        
    </cc:interface>
    
    <cc:implementation>
    
        <h:outputStylesheet library="cui" name="jquery.tagit.css" target="head" />
        <h:outputStylesheet library="cui" name="tagit.ui-zendesk.css" target="head" />
    
        <h:outputScript library="primefaces" name="jquery/jquery.js" target="head" />
        <h:outputScript library="cui" name="jquery-ui.min.js" target="head" />
        <h:outputScript library="cui" name="tag-it.min.js" target="head" />             
    
        <h:outputScript>                                
            $(function() {
                //Activate the tagit plugin with the allow spaces option. 
                //we insert the clientId to insure uniqueness of ids in case the component 
                //was used multiple times on the same page. We must escape JSF's column 
                //separator since it's considered a special character for jQuery
                var tagId = ("#" + "#{cc.clientId}:myTags".replace(/:/g, "\\:"));                           
                $(tagId).tagit({allowSpaces: true});                              
    
                //find the input element generated by the plugin and whenever it loses focus, 
                //update the hidden input with the values of all the tags separated by semi-columns
                //The hidden input is wired to the 'val' attribute and will feed the value to the backing bean             
                $("input[type='text'].ui-widget-content").blur(function() {                                                     
                    var allTags = "";
                    $('.tagit-hidden-field').each(function() {
                        allTags += $(this).val() + ";"                      
                    });
                    var hiddenInputId = ("#" + "#{cc.clientId}:tagValue".replace(/:/g, "\\:"));                                
                    $(hiddenInputId).val(allTags);
                });
            });                
        </h:outputScript>
    
        <h:inputHidden id="tagValue" value="#{cc.attrs.val}"/>
    
        <p>Hit Enter or Comma to separate tags</p>
    
        <ul id="#{cc.clientId}:myTags">         
        </ul>
    
    </cc:implementation>
    

    然后你可以像这样在页面上使用它:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html"  
        xmlns:cui="http://java.sun.com/jsf/composite/cui">
    <h:head></h:head>
    <h:body>
        <h:form id="form">      
            <cui:tagit val="#{page1.tags}" />                                   
            <h:commandButton value="Submit">
              <f:ajax execute="@form" render="output"/>
            </h:commandButton>
        </h:form>
    </h:body>
    </html>
    

    【讨论】:

    • 添加了一个完全符合您要求的工作示例。可能想从内联 javascript 中删除 cmets,因为某些版本的 Mojarra 不喜欢它。
    猜你喜欢
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 2011-07-21
    • 2014-09-08
    • 1970-01-01
    • 2018-08-14
    • 1970-01-01
    相关资源
    最近更新 更多