标记输入组件,支持标记名中的空格

时间:2014-12-01 10:43:41

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

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

enter image description here

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

enter image description here

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

2 个答案:

答案 0 :(得分:1)

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

代码&amp;感兴趣的功能:

<强> 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(这是;的代码)

注意:它可能会产生一些副作用,尤其是如果您在其他地方使用自动填充功能并且有所不同......

答案 1 :(得分:1)

看一下这个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>