JSF - 无需命中服务器的选择框的自动完成功能

时间:2014-02-24 07:20:12

标签: jquery json jsf jsf-2

我对客户端开发很新。虽然可能有一些解决方案已经可用。让我试着描述我的问题场景。我有一个JSF page(表单),其中存在不同的下降(选择框)。他们可以是国籍,语言或宗教。当我们输入代码时,它会选择国籍或语言。在这基本上是server hit goes(partial ajax request)。我的大四学生建议我完全开发这个auto-complete功能的客户端。他们不希望任何服务器受到攻击。

他们基本上想要的是,当我输入一些值时,它会从客户端加载建议列表,它不应该转到服务器。它应该比update the jsf component相应吗?我该如何处理这个问题?任何建议都有帮助。

当前项目基础有此代码 - 自定义组件

public class VisionSelectOneMenuRenderer extends InputRenderer {
  @Override
  public void decode(FacesContext context, UIComponent component) {
    VisionSelectOneMenu menu = (VisionSelectOneMenu) component;

    if (menu.isDisabled() || menu.isReadonly()) {
      return;
    }

    decodeBehaviors(context, menu);

    String clientId = menu.getClientId(context);
    String value = context.getExternalContext().getRequestParameterMap().get(clientId + "_selectBox");

    if (value != null) {
      menu.setSubmittedValue(value);
    }
  }

  @Override
  public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
    VisionSelectOneMenu menu = (VisionSelectOneMenu) component;

    encodeMarkup(context, menu);
    encodeScript(context, menu);
  }

  protected void encodeMarkup(FacesContext context, VisionSelectOneMenu menu) throws IOException {
    List<SelectItem> selectItems = getSelectItems(context, menu);
    String clientId = menu.getClientId(context);
    boolean disabled = menu.isDisabled();
    Class type = getValueType(context, menu);

    String styleclass = menu.getStyleClass();
    styleclass = styleclass == null ? VisionSelectOneMenu.STYLE_CLASS : VisionSelectOneMenu.STYLE_CLASS + " " + styleclass;
    styleclass = disabled ? styleclass + " ui-state-disabled" : styleclass;
    styleclass = "sfield";

    /* writer.startElement("div", menu);
writer.writeAttribute("id", clientId, "id");
writer.writeAttribute("class", styleclass, "styleclass");
if(style != null)
writer.writeAttribute("style", style, "style");    */

    encodeInput(context, menu, clientId, selectItems, type);
    //encodeLabel(context, menu, selectItems, type);
    //encodeMenuIcon(context, menu);
    // encodePanel(context, menu, selectItems, type);

    // writer.endElement("div");
  }

  protected void encodeInput(FacesContext context, VisionSelectOneMenu menu, String clientId, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String inputId = clientId + "_selectBox";

    writer.startElement("div", menu);
    writer.writeAttribute("class", "sfield", null);

    writer.startElement("select", menu);
    writer.writeAttribute("id", inputId, "id");
    writer.writeAttribute("tabindex", "-1", null);
    writer.writeAttribute("name", inputId, null);
    //writer.writeAttribute("class", "searchableSelectLookupSelect", null);
    if (menu.getOnchange() != null) {
      writer.writeAttribute("onchange", menu.getOnchange(), null);
    }
    if (menu.isDisabled()) {
      writer.writeAttribute("disabled", "disabled", null);
    }

    encodeSelectItems(context, menu, selectItems, type);

    writer.endElement("select");

    writer.startElement("input", null);
    writer.writeAttribute("id", clientId, null);
    //writer.writeAttribute("tabindex", "-1", null);
    writer.writeAttribute("name", clientId, null);
    writer.writeAttribute("type", "text", null);
    writer.writeAttribute("class", "searchableSelectLookupText excludeClear", null);
    if (menu.isDisabled()) {
        writer.writeAttribute("disabled", "disabled", null);
    }
    writer.endElement("input");

    writer.endElement("div");
  }

  protected void encodeLabel(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String label = getSelectedLabel(context, menu, selectItems, type);

    writer.startElement("a", null);
    writer.writeAttribute("href", "#", null);
    writer.writeAttribute("class", VisionSelectOneMenu.LABEL_CONTAINER_CLASS, null);

    if (menu.getTabindex() != null) {
      writer.writeAttribute("tabindex", menu.getTabindex(), null);
    }

    writer.startElement("label", null);
    writer.writeAttribute("class", VisionSelectOneMenu.LABEL_CLASS, null);

    if (label.equals("&nbsp;")) {
      writer.write(label);
    }
    else {
      writer.writeText(label, null);
    }

    writer.endElement("label");
    writer.endElement("a");
  }

  protected void encodeMenuIcon(FacesContext context, VisionSelectOneMenu menu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();

    writer.startElement("div", menu);
    //writer.writeAttribute("class", VisionSelectOneMenu.TRIGGER_CLASS, null);

    writer.startElement("span", menu);
    //writer.writeAttribute("class", "ui-icon ui-icon-triangle-1-s", null);
    writer.endElement("span");

    writer.endElement("div");
  }

  protected void encodePanel(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    boolean customContent = menu.getVar() != null;
    int height = calculatePanelHeight(menu, selectItems.size());

    writer.startElement("div", null);
    writer.writeAttribute("id", menu.getClientId(context) + "_panel", null);
    writer.writeAttribute("class", VisionSelectOneMenu.PANEL_CLASS, null);

    if (height != -1) {
      writer.writeAttribute("style", "height:" + height + "px", null);
    }

    if (customContent) {
      writer.startElement("table", menu);
      writer.writeAttribute("class", VisionSelectOneMenu.TABLE_CLASS, null);
      writer.startElement("tbody", menu);
      encodeOptionsAsTable(context, menu, selectItems, type);
      writer.endElement("tbody");
      writer.endElement("table");
    }
    else {
      writer.startElement("ul", menu);
      writer.writeAttribute("class", VisionSelectOneMenu.LIST_CLASS, null);
      encodeOptionsAsList(context, menu, selectItems, type);
      writer.endElement("ul");
    }

    writer.endElement("div");
  }

  protected void encodeOptionsAsTable(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String var = menu.getVar();
    List<Column> columns = menu.getColums();
    Object value = menu.getValue();

    for (SelectItem selectItem : selectItems) {
      Object itemValue = selectItem.getValue();
      Object coercedItemValue = null;

      if (itemValue != null && !itemValue.equals("")) {
        coercedItemValue = context.getApplication().getExpressionFactory().coerceToType(itemValue, type);
      }

      boolean selected = (value != null && value.equals(coercedItemValue));

      context.getExternalContext().getRequestMap().put(var, selectItem.getValue());

      String rowStyleClass = selected ? VisionSelectOneMenu.ROW_CLASS + " ui-state-active" : VisionSelectOneMenu.ROW_CLASS;

      writer.startElement("tr", null);
      writer.writeAttribute("class", rowStyleClass, null);

      if (itemValue instanceof String) {
        writer.startElement("td", null);
        writer.writeAttribute("colspan", columns.size(), null);
        writer.write(selectItem.getLabel());
        writer.endElement("td");
      }
      else {
        for (Column column : columns) {
          writer.startElement("td", null);
          column.encodeAll(context);
          writer.endElement("td");
        }
      }

      writer.endElement("tr");
    }

    context.getExternalContext().getRequestMap().put(var, null);
  }

  protected void encodeOptionsAsList(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    Object value = menu.getValue();

    for (int i = 0; i < selectItems.size(); i++) {
      SelectItem selectItem = selectItems.get(i);
      Object itemValue = selectItem.getValue();
      String itemLabel = selectItem.getLabel();
      Object coercedItemValue = null;
      itemLabel = isValueBlank(itemLabel) ? "&nbsp;" : itemLabel;

      if (itemValue != null && !itemValue.equals("")) {
        coercedItemValue = context.getApplication().getExpressionFactory().coerceToType(itemValue, type);
      }

      boolean selected = (i == 0 && value == null) || (value != null && value.equals(coercedItemValue));
      String itemStyleClass = selected ? VisionSelectOneMenu.ITEM_CLASS + " ui-state-active" : VisionSelectOneMenu.ITEM_CLASS;

      writer.startElement("li", null);
      writer.writeAttribute("class", itemStyleClass, null);

      if (itemLabel.equals("&nbsp;")) {
        writer.write(itemLabel);
      }
      else {
        writer.writeText(itemLabel, null);
      }

      writer.endElement("li");
    }
  }

  protected void encodeScript(FacesContext context, VisionSelectOneMenu menu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = menu.getClientId(context);

    writer.startElement("script", null);
    writer.writeAttribute("type", "text/javascript", null);
    //writer.write("var selectedText = '';");

    // TEXT box script
    //writer.write("jQuery(document).ready(function(){$('#"+clientId+"').val($('#"+clientId+"_selectBox option:selected').text());});" +
    writer.write(
        "jQuery(document).ready(function(){" +
            //" if($('#"+clientId+"_selectBox').is(':disabled')){ $('#"+clientId+"').attr('disabled','disabled') }" +
            "$('#" + clientId + "_selectBox').focus(function(){" +
            //"if(!$('#"+clientId+"_selectBox').is(':hover')){"+
            // " $('#"+clientId+"').val(''); $('#"+clientId+"').focus();} "+
            "});" +
            "$('#" + clientId + "').val($('#" + clientId + "_selectBox option:selected').text());" +
            /*"$('#"+clientId+"_selectBox option').each(function(){" +
        //"var idvalue = $(this).text().split('$')[0];" +
        "var level = $(this).text().split(' [')[0];" +
        "var levelCode = $(this).text().split(' [')[1];" +
        "var desc = level.concat('^');" +

        "if(null != levelCode && typeof(levelCode)!='undefined' && levelCode.length > 0) {" +
          "var subStr = levelCode.substring(0,3);" +
          "desc = desc.concat(levelCode.substring(0,levelCode.length-1));" +

          "$(this).attr('description',desc);" +
        "}"+

        //"$(this).attr('description',$(this).text());" +
        //"$(this).text(idvalue);" +
        "$('#"+clientId+"').val($('#"+clientId+"_selectBox option:selected').text());" +
        "}" +
        ")});" +*/

            "jQuery('#" + clientId + "').keyup(" +
            "function(event){ " +
            "if (event.keyCode == 13 || event.keyCode == 9) { " +
            "var enteredText = $('#" + clientId + "').val();" +
            "$('#" + clientId + "_selectBox > option').each(function(){" +
            "   var optionValue = $(this).val();" +
            "   var optionText = $(this).text();" +
            "   var optionDesc = $(this).attr('description');" +
            "   if(optionValue == enteredText) {" +
            "     $('#" + clientId + "').val(optionText);" +
            "     $('#" + clientId + "_selectBox').val(optionValue);" +
            "   }" +
            "   if(optionValue != 0) {" +
            "       var splitted = optionDesc.split('^');" +
            "       if(splitted[1] == enteredText.toUpperCase()) {" +
            "         $('#" + clientId + "').val(optionText);" +
            "         $('#" + clientId + "_selectBox').val(optionValue);" +
            "       }" +
            "   }" +
            " " +
            "});" +
            "}" +// if for keycode ends
            "});"
    );

    writer.write(
        "jQuery('#" + clientId + "').click(" +
            "function(){" +
            "$('#" + clientId + "').val('');" +
            "});"
    );


    /*writer.write("jQuery('#"+clientId+"').focus(" +
        "function(){" +
        "$('#"+clientId+"_selectBox').trigger('click');" +
        "});"
    );*/

    writer.write(
        "jQuery('#" + clientId + "').blur(" +
            "function(){" +
            "var enteredText = $('#" + clientId + "').val();" +
            "$('#" + clientId + "_selectBox > option').each(function(){" +
            "   var optionValue = $(this).val();" +
            "   var optionText = $(this).text();" +
            "   var optionDesc = $(this).attr('description');" +
            "   if(optionValue == enteredText) {" +
            "     $('#" + clientId + "').val(optionText);" +
            "     $('#" + clientId + "_selectBox').val(optionValue);" +
            "     jQuery('#" + clientId + "_selectBox').trigger('change'); " +
            "   }" +
            "   if(optionValue != 0) {" +
            "       var splitted = optionDesc.split('^');" +
            "       if(splitted[1] == enteredText.toUpperCase()) {" +
            "         $('#" + clientId + "').val(optionText);" +
            "         $('#" + clientId + "_selectBox').val(optionValue);  jQuery('#" + clientId + "_selectBox').trigger('change');" +
            "       }" +
            "   }" +
            " " +
            "});" +
            "       $('#" + clientId + "').val($('#" + clientId + "_selectBox option:selected').text());" +
            "});"
    );


    //writer.write("jQuery('#"+clientId+"_selectBox').onmouseover(this.size=this.length)");

    encodeClientBehaviors(context, menu);

    writer.endElement("script");
  }


  protected void encodeClientBehaviors(FacesContext context, ClientBehaviorHolder component) throws IOException {
    String script = "";
    ResponseWriter writer = context.getResponseWriter();
    String clientId = ((UIComponent) component).getClientId(context);


    //ClientBehaviors
    Map<String, List<ClientBehavior>> behaviorEvents = component.getClientBehaviors();

    if (!behaviorEvents.isEmpty()) {

      List<ClientBehaviorContext.Parameter> params = Collections.emptyList();

      for (Iterator<String> eventIterator = behaviorEvents.keySet().iterator(); eventIterator.hasNext(); ) {
        String event = eventIterator.next();

        for (Iterator<ClientBehavior> behaviorIter = behaviorEvents.get(event).iterator(); behaviorIter.hasNext(); ) {
          ClientBehavior behavior = behaviorIter.next();
          ClientBehaviorContext cbc = ClientBehaviorContext.createClientBehaviorContext(
              context,
              (UIComponent) component,
              event,
              clientId,
              params
          );
          script += behavior.getScript(cbc);    //could be null if disabled
        }
      }
    }

    // SELECT Box scripts
    writer.write(
        "jQuery('#" + clientId + "_selectBox').change(" +
            "function(){" +
            "var selectedValue = $('#" + clientId + "_selectBox option:selected').text();" +
            "     $('#" + clientId + "').val(selectedValue);" + script +
            "}" +
            ");});"
    );
  }

  protected void encodeSelectItems(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> selectItems, Class type)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    Converter converter = getConverter(context, menu);
    Object value = menu.getValue();

    for (SelectItem selectItem : selectItems) {
      Object itemValue = selectItem.getValue();
      String itemLabel = selectItem.getLabel();
      String tLabel = itemLabel;

      if (itemValue != null && !itemValue.equals("")) {
        itemValue = context.getApplication().getExpressionFactory().coerceToType(itemValue, type);
      }

      writer.startElement("option", null);
      writer.writeAttribute("description", tLabel, null);
      writer.writeAttribute("value", getOptionAsString(context, menu, converter, itemValue), null);


      if (value != null && value.equals(itemValue)) {
        writer.writeAttribute("selected", "selected", null);
      }

      //if((Long)itemValue != 0) {
      /*if((Long)itemValue == 209) {
        writer.write(itemLabel + " [AFG]");
      } else if((Long)itemValue == 203) {
        writer.write(itemLabel + " [PAK]");
      } else if((Long)itemValue == 205) {
        writer.write(itemLabel + " [IND]");
      } else {
        writer.write(itemLabel + " [" + itemValue + "]");
      }*/
      /*}
      else
        writer.write(itemLabel);*/

      //writer.write(itemLabel);
      if (itemLabel.contains("^")) {
        String[] formattedLabel = itemLabel.split("\\^");

        itemLabel = formattedLabel[0] + " [" + formattedLabel[1] + "]" + " [" + itemValue + "]";
        writer.write(itemLabel);
      }
      else {
        if (itemValue instanceof Long) {
          if ((Long) itemValue != 0) {
            writer.write(itemLabel + " [" + itemValue + "]");
          }
          else {
            writer.write(itemLabel);
          }
        }
        else {
          writer.write(itemLabel);
        }
      }

      writer.endElement("option");
    }
  }

  protected String getSelectedLabel(FacesContext context, VisionSelectOneMenu menu, List<SelectItem> items, Class type) {
    Object value = menu.getValue();
    String label = null;

    for (SelectItem item : items) {
      Object itemValue = item.getValue();
      if (itemValue != null && !itemValue.equals("")) {
        itemValue = context.getApplication().getExpressionFactory().coerceToType(item.getValue(), type);
      }

      if (value != null && value.equals(itemValue)) {
        label = item.getLabel();
        break;
      }
    }

    if (label == null) {
      label = !items.isEmpty() ? items.get(0).getLabel() : "&nbsp;";
    }

    return label;
  }

  protected int calculatePanelHeight(VisionSelectOneMenu menu, int itemSize) {
    int height = menu.getHeight();

    if (height != Integer.MAX_VALUE) {
      return height;
    }
    else if (itemSize > 10) {
      return 200;
    }

    return -1;
  }

  @Override
  public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException {
    //Rendering happens on encodeEnd
  }

  @Override
  public boolean getRendersChildren() {
    return true;
  }

  protected Class getValueType(FacesContext context, VisionSelectOneMenu menu) {
    ValueExpression ve = menu.getValueExpression("value");
    Class type = ve == null ? String.class : ve.getType(context.getELContext());

    return type == null ? String.class : type;
  }
}

感谢。

1 个答案:

答案 0 :(得分:1)

正如Oskars Pakers建议的那样,如果不访问服务器端,您将无法执行任何AJAX。在您的情况下,解决方案是将客户端的所有数据放在<option>标记中。然而,根据您拥有的数据量,加载速度可能非常慢,如果用户不需要,您的服务器会生成不必要的代码。如果您仍然决定这样做,那么在客户端模拟自动完成行为的最佳选择是使用Select2,如果您的老板改变了主意,他们也会使用AJAX功能。