Skip to content

Instantly share code, notes, and snippets.

@criedel
Created March 28, 2017 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save criedel/315d0cf8dab312870a7bc0d184ef0dd4 to your computer and use it in GitHub Desktop.
Save criedel/315d0cf8dab312870a7bc0d184ef0dd4 to your computer and use it in GitHub Desktop.
FireEvent Tapestry 5.3
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.ComponentEventCallback;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.internal.util.Holder;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.slf4j.Logger;
/**
* If assigned to any component it can trigger an event (see {@link #eventListener}). The trigger event on the client side can be freely chosen but must be one
* of the {@link #supportedClientEvents}. When used with text-fields or select boxes their value will be submitted as event context. This could be used for any
* server-side validation.
*/
@Import(library = { "fireEvent.js" })
public class FireEvent {
public static final String SELECT_COMPONENTID_PARAMETER = "t:selectvalue";
public static final String SELECT_TEXTFIELDID_PARAMETER = "t:textfieldvalue";
public static final String ELEMENTID_PARAMETER = "t:elementId";
/**
* The field component to which this mixin is attached.
*/
@InjectContainer
private ClientElement element;
@Inject
private ComponentResources componentResources;
@Inject
private Logger logger;
@Inject
private Request request;
@Environmental
private JavaScriptSupport scriptSupport;
/**
* The client side javascript event that triggers the {@link #eventListener}. Use 'change', 'click', etc. instead of 'onchange' or 'onclick'.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL, required = true, value = "click")
private String clientEvent;
/**
* Name of the event handler method that will be triggered in the component this mixin is applied to.
*/
@Parameter(defaultPrefix = BindingConstants.LITERAL, required = true, value = "fire")
private String eventListener;
@Parameter
private int delay;
@Parameter
private Object[] context;
@Parameter(defaultPrefix = BindingConstants.LITERAL, required = false)
private String zone;
/**
* Allows a specific implementation of {@link org.apache.tapestry5.ValueEncoder} to be supplied. This is used to create client-side string values for the
* different options.
*
* @see org.apache.tapestry5.services.ValueEncoderSource
*/
@Parameter
private ValueEncoder<?> contextEncoder;
protected Link createLink(Object[] context) {
if (context == null) {
context = new Object[1];
}
context = ArrayUtils.add(context, eventListener);
return componentResources.createEventLink("event", context);
}
Object onEvent(final Object... context) {
for (final Object o : context) {
logger.debug("onEvent parameter: {}", o);
}
for (final String s : request.getParameterNames()) {
logger.debug("request param {} = {}", s, request.getParameter(s));
}
final List<Object> parameters = new ArrayList<Object>();
for (int i = 0; i < context.length - 1; i++) {
if (contextEncoder != null) {
parameters.add(toValue((String) context[i]));
} else if (context[i] != null) {
parameters.add(context[i]);
}
}
// in case the mixin was used on a select component
final String selectComponentValue = this.request.getParameter(SELECT_COMPONENTID_PARAMETER);
if (selectComponentValue != null) {
parameters.add(selectComponentValue);
}
final String textFieldComponentValue = this.request.getParameter(SELECT_TEXTFIELDID_PARAMETER);
if (textFieldComponentValue != null) {
parameters.add(textFieldComponentValue);
}
// add the elemetn Id as last argument to the event context
parameters.add(this.request.getParameter(ELEMENTID_PARAMETER));
final Holder<Object> holder = Holder.create();
final ComponentEventCallback<Object> callback = result -> {
holder.put(result);
return true;
};
/*
* The last parameter should be the name of the event that we want to trigger.
*/
final String eventType = (String) context[context.length - 1];
this.componentResources.triggerEvent(eventType, parameters.toArray(), callback);
return holder.get();
}
protected Object toValue(final String submittedValue) {
return InternalUtils.isBlank(submittedValue) ? null : this.contextEncoder.toValue(submittedValue);
}
void afterRender() {
final Link link = createLink(context);
final JSONObject parameter = new JSONObject();
parameter.put("event", this.clientEvent);
parameter.put("link", link.toURI());
parameter.put("clientId", this.element.getClientId());
parameter.put("zoneId", this.zone);
parameter.put("delay", this.delay);
scriptSupport.addInitializerCall("bindEventURL", parameter);
}
}
(function( $ ) {
'use strict';
$.extend(Tapestry.Initializer, {
/**
* Binds an event handler on an element. Optionally updates a specified zone.
*
* @param spec.event
* event to bind on
* @param spec.clientId
* the element's id that should listen to the event
* @param spec.link
* component event request URL
* @param spec.zoneId
* id of element to update when select is changed
*/
bindEventURL : function(spec) {
var element = spec.clientId;
var event = spec.event;
var zoneId = spec.zoneId;
var url = spec.link;
var el = $('#' + element);
var timeoutId;
var findZone = function(zoneId) {
return zoneId === '^' ? $(el).closest('.t-zone') : $('#' + zoneId);
};
var zoneElement = findZone(zoneId);
var eventHandler = function(e) {
if (!zoneElement || !zoneElement.length) {
zoneElement = findZone(zoneId);
}
var parameters = {};
if (el.is('select')) {
var selectValue = $('#' + element + ' :selected').val();
if (selectValue) {
parameters['t:selectvalue'] = selectValue;
}
parameters['event'] = event;
} else if (el.is('input')) {
var value = $('#' + element).val();
if (value) {
parameters['t:textfieldvalue'] = value;
}
parameters['event'] = event;
}
zoneElement.tapestryZone('update' , {url : url, params : parameters});
// reset timeout
timeoutId = undefined;
return false;
};
var ignoreKeyCodes = [ 16, 17, 18, 20, 27, 37, 38, 39, 40, 91, 93 ];
el.on(event, {}, function(e) {
if (event === 'keyup' && $.inArray(e.keyCode, ignoreKeyCodes) > -1 ) {
return false;
}
if (spec.delay) {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(eventHandler, spec.delay, e);
} else {
eventHandler(e);
}
return false;
});
}
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment