All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wicketstuff.select2.AbstractSelect2Choice Maven / Gradle / Ivy

There is a newer version: 10.2.0
Show newest version
/*
 * Copyright 2012 Igor Vaynberg
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
 * the License. You may obtain a copy of the License in the LICENSE file, or at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.wicketstuff.select2;

import com.github.openjson.JSONException;
import com.github.openjson.JSONStringer;
import org.apache.wicket.IRequestListener;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.Strings;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Base class for Select2 components
 *
 * @param 
 *            type of choice object
 * @param 
 *            type of model object
 * @author igor
 */
public abstract class AbstractSelect2Choice extends FormComponent implements IRequestListener
{
	private static final long serialVersionUID = 1L;

	private final Select2Behavior select2Behavior = new Select2Behavior();

	private ChoiceProvider provider;

	private boolean convertInputPerformed = false;


	/**
	 * @param id
	 * 		markup id
	 */
	public AbstractSelect2Choice(String id)
	{
		this(id, null, null);
	}

	/**
	 * Constructor
	 *
	 * @param id
	 *            component id
	 * @param model
	 *            component model
	 */
	public AbstractSelect2Choice(String id, IModel model)
	{
		this(id, model, null);
	}


	/**
	 * Constructor.
	 *
	 * @param id
	 *            component id
	 * @param provider
	 *            choice provider
	 */
	public AbstractSelect2Choice(String id, ChoiceProvider provider)
	{
		this(id, null, provider);
	}

	/**
	 * Construct.
	 *
	 * @param id
	 *            markup id
	 * @param model
	 *            model for select
	 * @param provider
	 *            choice provider
	 */
	public AbstractSelect2Choice(String id, IModel model, ChoiceProvider provider)
	{
		super(id, model);
		this.provider = provider;
		add(select2Behavior);
		setOutputMarkupId(true);
	}

	/**
	 * Returns the {@link Settings}. The settings
	 * 
* closeOnSelect = true
* escapeMarkup = "Utils.escapeMarkup"
* width = {@link Settings.Widths#RESOLVE}; *

* are the the Select2 default as documented on https://select2.org/configuration/options-api, which are different from the default in {@link Settings} * * @return Select2 settings for this component. */ public final Settings getSettings() { return select2Behavior.getSettings(); } /** * Sets the choice provider * * @param provider * provider to set */ public final void setProvider(ChoiceProvider provider) { this.provider = provider; } /** * @return choice provider */ public final ChoiceProvider getProvider() { if (provider == null) { throw new IllegalStateException("Select2 choice component: " + getId() + " does not have a ChoiceProvider set"); } return provider; } @Override public final void convertInput() { // AbstractSelect2Choice uses ChoiceProvider to convert IDS into objects. // The #getConverter() method is not supported by Select2Choice. String[] inputAsArray = getInputAsArray(); M convertedInput = convertValue(inputAsArray); setConvertedInput(convertedInput); convertInputPerformed = true; } /** * Convert IDS into choices. * * @param ids * list of identities * @return collection of choices or empty collection */ protected final Collection convertIdsToChoices(List ids) { if (ids == null || ids.isEmpty()) { return Collections.emptyList(); } return getProvider().toChoices(ids); } /** * Gets the markup id that is safe to use in jQuery by escaping dots in the default * {@link #getMarkup()} * * @return markup id */ protected String getJquerySafeMarkupId() { return getMarkupId().replace(".", "\\\\."); } /** * Escapes single quotes in localized strings to be used as JavaScript strings enclosed in * single quotes * * @param key * resource key for localized message * @return localized string with escaped single quotes */ protected String getEscapedJsString(String key) { String value = getString(key); return Strings.replaceAll(value, "'", "\\'").toString(); } /** * @return current value, suitable for rendering as selected value in select2 component */ protected final M getCurrentValue() { if (hasRawInput()) { // since raw input is retained, we need explicitly convert it to target value if (convertInputPerformed) { return getConvertedInput(); } else { String raw = getRawInput(); M result = null; if (raw != null) { String[] values = raw.split(FormComponent.VALUE_SEPARATOR); result = convertValue(values); } return result; } } else { return getModelObject(); } } @Override protected void onInitialize() { super.onInitialize(); // configure the ajax callbacks if (isAjax() && !getSettings().isStateless()) { AjaxSettings ajax = getSettings().getAjax(true); ajax.setData(String.format( "function(params) { return { '%s': params.term, page: params.page, '%s':true, '%s':[window.location.protocol, '//', window.location.host, window.location.pathname].join('')}; }", getSettings().getQueryParam(), WebRequest.PARAM_AJAX, WebRequest.PARAM_AJAX_BASE_URL)); ajax.setProcessResults("function(data, page) { return { results: data.items, pagination: { more: data.more } }; }"); } else if (getSettings().isStateless()) //configure stateless mode { AjaxSettings ajax = getSettings().getAjax(true); ajax.setProcessResults("function(data, page) { return { results: data.items, pagination: { more: data.more } }; }"); } } @Override protected void onConfigure() { super.onConfigure(); if(getSettings().isStateless()) { if(Strings.isEmpty(getSettings().getMountPath())) { throw new IllegalStateException("Select2 in stateless mode should specify a mountPath"); } getSettings().getAjax(true).setUrl(getSettings().getMountPath()); } else if (isAjax()) { getSettings().getAjax(true).setUrl(urlForListener(null)); } } @Override public void onEvent(IEvent event) { super.onEvent(event); if (event.getPayload() instanceof AjaxRequestTarget) { AjaxRequestTarget target = (AjaxRequestTarget)event.getPayload(); if (target.getComponents().contains(this)) { // if this component is being repainted by ajax, directly, we // must destroy Select2 so it removes // its elements from DOM target.prependJavaScript(JQuery.execute("$('#%s').select2('destroy');", getJquerySafeMarkupId())); } } } @Override protected boolean getStatelessHint() { if(getSettings().isStateless()) { return true; } return !isAjax(); } public boolean isAjax() { return provider != null; } /** * Utility method to generate JSON response. * * @param provider * @param outputStream */ public static void generateJSON(ChoiceProvider provider, OutputStream outputStream) { generateJSON(Settings.DEFAULT_QUERY_PARAM, provider, outputStream); } /** * Utility method to generate JSON response. * * @param provider * @param outputStream * @param queryParam - parameter to be used as Ajax query param */ public static void generateJSON(String queryParam, ChoiceProvider provider, OutputStream outputStream) { // this is the callback that retrieves matching choices used to populate the dropdown Request request = RequestCycle.get().getRequest(); IRequestParameters params = request.getRequestParameters(); // retrieve choices matching the search term String term = params.getParameterValue(queryParam).toOptionalString(); int page = params.getParameterValue("page").toInt(1); // select2 uses 1-based paging, but in wicket world we are used to // 0-based page -= 1; Response response = new Response<>(); provider.query(term, page, response); // jsonize and write out the choices to the response OutputStreamWriter out = new OutputStreamWriter(outputStream, request.getCharset()); JSONStringer json = new JSONStringer(); try { json.object(); json.key("items").array(); for (T item : response) { json.object(); provider.toJson(item, json); json.endObject(); } json.endArray(); json.key("more").value(response.getHasMore()).endObject(); out.write(json.toString()); } catch (JSONException | IOException e) { throw new RuntimeException("Could not write Json response", e); } try { out.flush(); } catch (IOException e) { throw new RuntimeException("Could not write Json to servlet response", e); } try { out.close(); } catch (IOException e) { throw new RuntimeException("Could not write Json to servlet response", e); } } @Override public void onRequest() { WebResponse webResponse = (WebResponse) getRequestCycle().getResponse(); webResponse.setContentType("application/json"); generateJSON(getSettings().getQueryParam(), provider, webResponse.getOutputStream()); } @Override protected void onDetach() { convertInputPerformed = false; if (isAjax()) { getProvider().detach(); } super.onDetach(); } /** * Append a single option markup. * * @param buffer buffer to append to * @param choice choice to create option markup for */ protected void appendOptionHtml(final AppendingStringBuffer buffer, T choice) { buffer.append(""); } /** * Processes the component tag. * * @param tag * Tag to modify * @see org.apache.wicket.Component#onComponentTag(ComponentTag) */ @Override protected void onComponentTag(ComponentTag tag) { // Must be attached to an select tag checkComponentTag(tag, "select"); // Default handling for component tag super.onComponentTag(tag); } /** * this method will add default options * * @param markupStream * The markup stream * @param openTag * The open tag for the body */ @Override public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) { if (provider == null) { super.onComponentTagBody(markupStream, openTag); } else { M currentValue = getCurrentValue(); replaceComponentTagBody(markupStream, openTag, createOptionsHtml(currentValue)); } } /** * Creates the HTML option(s) markup representing the current value. * * @param currentValue * the current value * @return * the HTML markup for any option(s) */ protected abstract CharSequence createOptionsHtml(M currentValue); /** * Empty input is acceptable */ @Override public boolean isInputNullable() { return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy