Author: nbelaevski
Date: 2010-07-08 09:20:35 -0400 (Thu, 08 Jul 2010)
New Revision: 17770
Modified:
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/component/AbstractComboBox.java
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/renderkit/ComboBoxRendererBase.java
root/ui-sandbox/inputs/trunk/combobox/src/main/resources/META-INF/resources/org.richfaces/ComboBox.js
root/ui-sandbox/inputs/trunk/combobox/src/main/templates/comboBox.template.xml
Log:
AJAXed comboBox functionality
Modified:
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/component/AbstractComboBox.java
===================================================================
---
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/component/AbstractComboBox.java 2010-07-08
09:45:35 UTC (rev 17769)
+++
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/component/AbstractComboBox.java 2010-07-08
13:20:35 UTC (rev 17770)
@@ -21,11 +21,15 @@
*/
package org.richfaces.component;
+import java.io.IOException;
import java.util.List;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import org.richfaces.cdk.annotations.Attribute;
@@ -33,14 +37,19 @@
import org.richfaces.cdk.annotations.Signature;
import org.richfaces.cdk.annotations.Tag;
import org.richfaces.cdk.annotations.TagType;
+import org.richfaces.context.ExtendedVisitContext;
+import org.richfaces.context.ExtendedVisitContextMode;
+import org.richfaces.renderkit.MetaComponentRenderer;
/**
* @author Nick Belaevski
- *
+ *
*/
@JsfComponent(tag = @Tag(type = TagType.Facelets, handler =
"org.richfaces.view.facelets.ComboBoxHandler"))
-public abstract class AbstractComboBox extends UIInput {
+public abstract class AbstractComboBox extends UIInput implements MetaComponentResolver,
MetaComponentEncoder {
+ public static final String ITEMS_META_COMPONENT_ID = "items";
+
public static final String COMPONENT_TYPE = "org.richfaces.ComboBox";
public static final String COMPONENT_FAMILY = UIInput.COMPONENT_FAMILY;
@@ -55,4 +64,34 @@
@Attribute(literal = true)
public abstract String getVar();
+
+ public String resolveClientId(FacesContext facesContext, UIComponent
contextComponent, String metaComponentId) {
+ if (ITEMS_META_COMPONENT_ID.equals(metaComponentId)) {
+ return getClientId(facesContext) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR + metaComponentId;
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean visitTree(VisitContext context, VisitCallback callback) {
+ if (context instanceof ExtendedVisitContext) {
+ ExtendedVisitContext extendedVisitContext = (ExtendedVisitContext) context;
+ if (extendedVisitContext.getVisitMode() == ExtendedVisitContextMode.RENDER)
{
+
+ VisitResult result =
extendedVisitContext.invokeMetaComponentVisitCallback(this, callback,
ITEMS_META_COMPONENT_ID);
+ if (result == VisitResult.COMPLETE) {
+ return true;
+ } else if (result == VisitResult.REJECT) {
+ return false;
+ }
+ }
+ }
+
+ return super.visitTree(context, callback);
+ }
+
+ public void encodeMetaComponent(FacesContext context, String metaComponentId) throws
IOException {
+ ((MetaComponentRenderer) getRenderer(context)).encodeMetaComponent(context, this,
metaComponentId);
+ }
}
Modified:
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/renderkit/ComboBoxRendererBase.java
===================================================================
---
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/renderkit/ComboBoxRendererBase.java 2010-07-08
09:45:35 UTC (rev 17769)
+++
root/ui-sandbox/inputs/trunk/combobox/src/main/java/org/richfaces/renderkit/ComboBoxRendererBase.java 2010-07-08
13:20:35 UTC (rev 17770)
@@ -31,19 +31,24 @@
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.PartialViewContext;
import javax.faces.context.ResponseWriter;
+import javax.faces.convert.Converter;
-import org.ajax4jsf.renderkit.RendererBase;
import org.ajax4jsf.renderkit.RendererUtils.HTML;
+import org.ajax4jsf.util.SelectUtils;
import org.richfaces.component.AbstractComboBox;
+import org.richfaces.component.MetaComponentResolver;
import com.google.common.collect.Iterators;
/**
* @author Nick Belaevski
- *
+ *
*/
@ResourceDependencies({
+ @ResourceDependency(library = "javax.faces", name = "jsf.js"),
@ResourceDependency(name = "jquery.js"),
@ResourceDependency(name = "jquery.position.js"),
@ResourceDependency(name = "richfaces.js"),
@@ -53,19 +58,20 @@
@ResourceDependency(library = "org.richfaces", name =
"SelectBase.js"),
@ResourceDependency(library = "org.richfaces", name =
"ComboBox.js"),
@ResourceDependency(library = "org.richfaces", name =
"ComboBox.ecss")
-
+
})
-public class ComboBoxRendererBase extends RendererBase {
+public abstract class ComboBoxRendererBase extends InputRendererBase implements
MetaComponentRenderer {
//TODO nick - handle parameter
private Iterator<Object> getItems(FacesContext facesContext, AbstractComboBox
component) {
Object itemsObject = null;
-
+
MethodExpression autocompleteMethod = component.getAutocompleteMethod();
if (autocompleteMethod != null) {
try {
- itemsObject = autocompleteMethod.invoke(facesContext.getELContext(),
- new Object[] {facesContext, component, null});
+ String value = getInputValue(facesContext, component);
+ itemsObject = autocompleteMethod.invoke(facesContext.getELContext(),
+ new Object[] {facesContext, component, value});
} catch (ELException e) {
// TODO: handle exception
e.printStackTrace();
@@ -75,7 +81,7 @@
}
Iterator<Object> result;
-
+
//TODO nick - primitive arrays support
if (itemsObject instanceof Object[]) {
result = Iterators.forArray((Object[]) itemsObject);
@@ -84,32 +90,44 @@
} else {
result = Iterators.emptyIterator();
}
-
+
return result;
}
-
+
private Object saveVar(FacesContext context, String var) {
if (var != null) {
Map<String, Object> requestMap =
context.getExternalContext().getRequestMap();
return requestMap.get(var);
}
-
+
return null;
}
-
+
private void setVar(FacesContext context, String var, Object varObject) {
if (var != null) {
Map<String, Object> requestMap =
context.getExternalContext().getRequestMap();
requestMap.put(var, varObject);
}
}
-
+
+ protected void encodeItemContainerBegin(FacesContext facesContext, AbstractComboBox
comboBox, Object item) throws IOException {
+ ResponseWriter writer = facesContext.getResponseWriter();
+
+ writer.startElement(HTML.LI_ELEMENT, comboBox);
+ writer.writeAttribute(HTML.CLASS_ATTRIBUTE, "cb_option cb_font
rf-ac-i", null);
+ }
+
+ protected void encodeItemContainerEnd(FacesContext facesContext, AbstractComboBox
comboBox, Object item) throws IOException {
+ ResponseWriter writer = facesContext.getResponseWriter();
+ writer.endElement(HTML.LI_ELEMENT);
+ }
+
protected void encodeItem(FacesContext facesContext, AbstractComboBox comboBox,
Object item) throws IOException {
ResponseWriter writer = facesContext.getResponseWriter();
-
+
writer.startElement(HTML.LI_ELEMENT, comboBox);
writer.writeAttribute(HTML.CLASS_ATTRIBUTE, "cb_option cb_font
rf-ac-i", null);
-
+
if (comboBox.getChildCount() > 0) {
for (UIComponent child: comboBox.getChildren()) {
child.encodeAll(facesContext);
@@ -120,40 +138,87 @@
writer.writeText(item, null);
}
}
-
+
writer.endElement(HTML.LI_ELEMENT);
}
-
+
+ protected void encodeFakeItem(FacesContext context, UIComponent component) throws
IOException {
+ ResponseWriter responseWriter = context.getResponseWriter();
+ responseWriter.startElement(HTML.LI_ELEMENT, component);
+ responseWriter.writeAttribute(HTML.STYLE_ATTRIBUTE, "display:none",
null);
+ responseWriter.endElement(HTML.LI_ELEMENT);
+ }
+
protected void encodeItems(FacesContext facesContext, UIComponent component) throws
IOException {
AbstractComboBox comboBox = (AbstractComboBox) component;
-
- ResponseWriter responseWriter = facesContext.getResponseWriter();
- responseWriter.startElement(HTML.UL_ELEMENT, component);
- responseWriter.writeAttribute(HTML.ID_ATTRIBUTE,
component.getClientId(facesContext)+"Items", null);
- responseWriter.writeAttribute(HTML.CLASS_ATTRIBUTE, "cb_list_ul",
null);
+ encodeItemsContainerBegin(facesContext, component);
+
boolean hasEncodedElements = false;
Object savedVar = saveVar(facesContext, comboBox.getVar());
-
+
for (Iterator<Object> items = getItems(facesContext, comboBox);
items.hasNext(); ) {
hasEncodedElements = true;
-
+
Object nextItem = items.next();
setVar(facesContext, comboBox.getVar(), nextItem);
-
+
encodeItem(facesContext, comboBox, nextItem);
}
setVar(facesContext, comboBox.getVar(), savedVar);
-
+
if (!hasEncodedElements) {
- responseWriter.startElement(HTML.LI_ELEMENT, component);
- responseWriter.writeAttribute(HTML.STYLE_ATTRIBUTE, "display:none",
null);
- responseWriter.endElement(HTML.LI_ELEMENT);
+ //TODO nick - ?
}
-
+
+ encodeItemsContainerEnd(facesContext, component);
+ }
+
+ protected String getContainerElementId(FacesContext facesContext, UIComponent
component) {
+ return component.getClientId(facesContext) + "Items";
+ }
+
+ protected void encodeItemsContainerBegin(FacesContext facesContext, UIComponent
component) throws IOException {
+ ResponseWriter responseWriter = facesContext.getResponseWriter();
+ responseWriter.startElement(HTML.UL_ELEMENT, component);
+ responseWriter.writeAttribute(HTML.ID_ATTRIBUTE,
getContainerElementId(facesContext, component), null);
+ responseWriter.writeAttribute(HTML.CLASS_ATTRIBUTE, "cb_list_ul",
null);
+ }
+
+ protected void encodeItemsContainerEnd(FacesContext facesContext, UIComponent
component) throws IOException {
+ ResponseWriter responseWriter = facesContext.getResponseWriter();
responseWriter.endElement(HTML.UL_ELEMENT);
}
-
+
+ protected void encodeItemsContainer(FacesContext facesContext, UIComponent component)
throws IOException {
+ encodeItemsContainerBegin(facesContext, component);
+ encodeFakeItem(facesContext, component);
+ encodeItemsContainerEnd(facesContext, component);
+ }
+
+ @Override
+ protected void doDecode(FacesContext context, UIComponent component) {
+ super.doDecode(context, component);
+
+ Map<String, String> requestParameters =
context.getExternalContext().getRequestParameterMap();
+ if (requestParameters.get(component.getClientId(context) + ".ajax") !=
null) {
+ PartialViewContext pvc = context.getPartialViewContext();
+ pvc.getRenderIds().add(component.getClientId(context) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR +
AbstractComboBox.ITEMS_META_COMPONENT_ID);
+ }
+ }
+
+ public void encodeMetaComponent(FacesContext context, UIComponent component, String
metaComponentId)
+ throws IOException {
+ if (AbstractComboBox.ITEMS_META_COMPONENT_ID.equals(metaComponentId)) {
+
+ PartialResponseWriter partialWriter =
context.getPartialViewContext().getPartialResponseWriter();
+ partialWriter.startUpdate(getContainerElementId(context, component));
+ encodeItems(context, component);
+ partialWriter.endUpdate();
+ } else {
+ throw new IllegalArgumentException(metaComponentId);
+ }
+ }
}
Modified:
root/ui-sandbox/inputs/trunk/combobox/src/main/resources/META-INF/resources/org.richfaces/ComboBox.js
===================================================================
---
root/ui-sandbox/inputs/trunk/combobox/src/main/resources/META-INF/resources/org.richfaces/ComboBox.js 2010-07-08
09:45:35 UTC (rev 17769)
+++
root/ui-sandbox/inputs/trunk/combobox/src/main/resources/META-INF/resources/org.richfaces/ComboBox.js 2010-07-08
13:20:35 UTC (rev 17770)
@@ -1,16 +1,16 @@
(function ($, rf) {
rf.utils = rf.utils || {};
-
+
rf.utils.Cache = function (data, options) {
this.key = options.key;
this.cache = {}
this.cache[this.key] = data || [];
this.values = options.parse && options.parse(data) || this.cache[this.key];
};
-
+
var getItems = function (key) {
var newCache = [];
-
+
if (this.cache[key]) {
newCache = this.cache[key];
} else {
@@ -31,21 +31,21 @@
}
}
}
-
+
return newCache;
};
-
+
$.extend(rf.utils.Cache.prototype, (function () {
return {
getItems: getItems
};
})());
-
+
})(jQuery, RichFaces);
(function ($, rf) {
- rf.ui = rf.ui || {};
+ rf.ui = rf.ui || {};
// Constructor definition
rf.ui.ComboBox = function(componentId, fieldId, options) {
this.namespace = "."+rf.Event.createNamespace(this.name, this.componentId);
@@ -60,31 +60,31 @@
bindEventHandlers.call(this);
updateItemsList.call(this, "");
};
-
+
var $p ={};
-
+
// Extend component class and add protected methods from parent class to our container
$p = rf.ui.SelectBase.extend(rf.ui.SelectBase, rf.ui.ComboBox, $p);
-
+
// define super class link
var $super = rf.ui.ComboBox.$super;
-
+
var defaultOptions = {
selectedItemClass:'cb_select',
autoFill:true,
minChars:0,
selectFirst:false
};
-
+
var ID = {
SELECT:'List',
ITEMS:'Items'
};
-
+
var bindEventHandlers = function () {
rf.Event.bind(rf.getDomElement(this.componentId+ID.ITEMS).parentNode,
"mouseover"+this.namespace, onMouseOver, this);
};
-
+
var onMouseOver = function(event) {
console && console.log && console.log("mouseOver");
var element = $(event.target).closest(".rf-ac-i",
event.currentTarget).get(0);
@@ -96,15 +96,15 @@
}
}
};
-
+
var updateItemsList = function (value) {
this.items =
$(rf.getDomElement(this.componentId+ID.ITEMS)).find(".rf-ac-i");
this.cache = new rf.utils.Cache(this.items, {
parse: getData,
key: value
- });
+ });
};
-
+
var getData = function (nodeList) {
var data = [];
nodeList.each(function () {
@@ -112,7 +112,7 @@
});
return data;
};
-
+
var scrollToSelectedItem = function() {
var offset = 0;
this.items.slice(0, this.index).each(function() {
@@ -128,7 +128,7 @@
}
}
};
-
+
var autoFill = function (inputValue, value) {
if( this.options.autoFill) {
var field = rf.getDomElement(this.fieldId);
@@ -136,7 +136,15 @@
rf.Selection.set(field, inputValue.length, field.value.length);
}
};
-
+
+ var callAjax = function(event) {
+ //caution: JSF submits inputs with empty names causing "WARNING: Parameters:
Invalid chunk ignored." in Tomcat log
+ var params = {};
+ params[this.componentId + ".ajax"] = "1";
+
+ rf.ajax(this.componentId, event, {parameters: params});
+ };
+
// Add new properties and methods
$.extend(rf.ui.ComboBox.prototype, (function () {
return {
@@ -149,19 +157,19 @@
getNamespace: function () {
return this.namespace;
},
-
+
selectItem: function(index, isOffset, noAutoFill) {
if (this.items.length==0) return;
-
+
if (this.index!=-1) {
this.items.eq(this.index).removeClass(this.options.selectedItemClass);
}
-
+
if (index==undefined) {
this.index = -1;
return;
}
-
+
if (isOffset) {
this.index += index;
if ( this.index<0 ) {
@@ -183,7 +191,7 @@
scrollToSelectedItem.call(this);
!noAutoFill && autoFill.call(this, this.newValue, item.text());
},
-
+
selectPrevItem: function () {
this.selectItem(-1, true);
},
@@ -191,17 +199,18 @@
this.selectItem(1, true);
},
selectPageUp: function () {
-
+
},
selectPageDown: function () {
-
+
},
onBeforeShow: function (event) {
+ callAjax.call(this, event);
},
-
+
changeValue: function (event, value) {
// TODO: ajax call here if needed
-
+
this.selectItem();
var newItems = this.cache.getItems(value);
this.items = $(newItems);
@@ -213,7 +222,7 @@
this.selectItem(0, false, event.which == rf.KEYS.BACKSPACE);
}
},
-
+
onShow: function (event) {
if (this.items && this.items.length>0) {
//??TODO it's nessesary only if not changed value
@@ -222,7 +231,7 @@
}
}
},
-
+
onHide: function () {
this.selectItem();
}
Modified: root/ui-sandbox/inputs/trunk/combobox/src/main/templates/comboBox.template.xml
===================================================================
---
root/ui-sandbox/inputs/trunk/combobox/src/main/templates/comboBox.template.xml 2010-07-08
09:45:35 UTC (rev 17769)
+++
root/ui-sandbox/inputs/trunk/combobox/src/main/templates/comboBox.template.xml 2010-07-08
13:20:35 UTC (rev 17770)
@@ -16,7 +16,7 @@
<cc:implementation>
<div id="#{clientId}" class="cb_field_width cb_field">
<div style="position : relative; overflow : hidden; text-align :
left; padding-right : 21px;">
- <input id="#{clientId}Input" type="text"
class="cb_font cb_input" />
+ <input id="#{clientId}Input" name="#{clientId}"
type="text" class="cb_font cb_input" />
<div id="#{clientId}Button" class="cb_button">
<div class="cb_button_arrow"></div>
</div>
@@ -33,7 +33,7 @@
<div class="cb_list_decoration">
<div class="cb_list_scroll cb_list_width
cb_list_height">
<cdk:body>
- <cdk:call expression="encodeItems(facesContext,
component)"/>
+ <cdk:call
expression="encodeItemsContainer(facesContext, component)"/>
</cdk:body>
</div>
</div>