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

org.wicketstuff.kendo.ui.datatable.DataTableBehavior Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License 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.kendo.ui.datatable;

import java.util.Collections;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.CallbackParameter;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Generics;
import org.wicketstuff.jquery.core.JQueryEvent;
import org.wicketstuff.jquery.core.Options;
import org.wicketstuff.jquery.core.ajax.IJQueryAjaxAware;
import org.wicketstuff.jquery.core.ajax.JQueryAjaxBehavior;
import org.wicketstuff.jquery.core.utils.ListUtils;
import org.wicketstuff.jquery.core.utils.RequestCycleUtils;
import org.wicketstuff.kendo.ui.KendoDataSource;
import org.wicketstuff.kendo.ui.KendoUIBehavior;
import org.wicketstuff.kendo.ui.datatable.DataSourceEvent.CreateEvent;
import org.wicketstuff.kendo.ui.datatable.DataSourceEvent.DeleteEvent;
import org.wicketstuff.kendo.ui.datatable.DataSourceEvent.UpdateEvent;
import org.wicketstuff.kendo.ui.datatable.button.CommandAjaxBehavior;
import org.wicketstuff.kendo.ui.datatable.button.CommandButton;
import org.wicketstuff.kendo.ui.datatable.button.ToolbarAjaxBehavior;
import org.wicketstuff.kendo.ui.datatable.button.ToolbarButton;
import org.wicketstuff.kendo.ui.datatable.button.CommandAjaxBehavior.CommandClickEvent;
import org.wicketstuff.kendo.ui.datatable.button.ToolbarAjaxBehavior.ToolbarClickEvent;
import org.wicketstuff.kendo.ui.datatable.column.CheckboxColumn;
import org.wicketstuff.kendo.ui.datatable.column.CommandColumn;
import org.wicketstuff.kendo.ui.datatable.column.IColumn;
import org.wicketstuff.kendo.ui.datatable.column.IdPropertyColumn;
import org.wicketstuff.kendo.ui.repeater.ChangeEvent;

import com.github.openjson.JSONObject;

/**
 * Provides a {@value #METHOD} behavior
* * @author Sebastien Briquet - sebfz1 */ public abstract class DataTableBehavior extends KendoUIBehavior implements IJQueryAjaxAware { private static final long serialVersionUID = 1L; public static final String METHOD = "kendoGrid"; private final IDataTableListener listener; private final IModel> columns; private KendoDataSource dataSource; // TODO: private JQueryAjaxBehavior onEditAjaxBehavior; private JQueryAjaxBehavior onCancelAjaxBehavior; private JQueryAjaxBehavior onChangeAjaxBehavior = null; private JQueryAjaxBehavior onCheckedAjaxBehavior = null; private JQueryAjaxBehavior onColumnReorderAjaxBehavior = null; private DataSourceAjaxBehavior onCreateAjaxBehavior; private DataSourceAjaxBehavior onUpdateAjaxBehavior; private DataSourceAjaxBehavior onDeleteAjaxBehavior; /** * Constructor * * @param selector the html selector (ie: "#myId") * @param columns the list of {@link IColumn} * @param listener the {@link IDataTableListener} */ public DataTableBehavior(String selector, IModel> columns, IDataTableListener listener) { this(selector, new Options(), columns, listener); } /** * Constructor * * @param selector the html selector (ie: "#myId") * @param options the {@link Options} * @param columns the list of {@link IColumn} * @param listener the {@link IDataTableListener} */ public DataTableBehavior(String selector, Options options, IModel> columns, IDataTableListener listener) { super(selector, METHOD, options); this.columns = columns; this.listener = Args.notNull(listener, "listener"); } // Methods // @Override public void bind(Component component) { super.bind(component); // data source // this.dataSource = new KendoDataSource(component); this.add(this.dataSource); // grid events // this.onCancelAjaxBehavior = this.newOnCancelAjaxBehavior(this); component.add(this.onCancelAjaxBehavior); if (this.listener.isSelectable()) { this.onChangeAjaxBehavior = this.newOnChangeAjaxBehavior(this); component.add(this.onChangeAjaxBehavior); } if (this.hasCheckboxColumn()) { this.onCheckedAjaxBehavior = this.newOnCheckedAjaxBehavior(this); component.add(this.onCheckedAjaxBehavior); } if (this.isColumnReorderEnabled()) { this.onColumnReorderAjaxBehavior = this.newColumnReorderAjaxBehavior(this); component.add(this.onColumnReorderAjaxBehavior); } // data events // this.onCreateAjaxBehavior = this.newOnCreateAjaxBehavior(this); component.add(this.onCreateAjaxBehavior); this.onUpdateAjaxBehavior = this.newOnUpdateAjaxBehavior(this); component.add(this.onUpdateAjaxBehavior); this.onDeleteAjaxBehavior = this.newOnDeleteAjaxBehavior(this); component.add(this.onDeleteAjaxBehavior); // toolbar buttons // for (ToolbarButton button : this.getVisibleToolbarButtons()) { if (!button.isBuiltIn()) { component.add(this.newToolbarAjaxBehavior(this, button)); } } // column buttons // for (CommandButton button : this.getVisibleCommandButtons()) { if (!button.isBuiltIn()) { component.add(this.newCommandAjaxBehavior(this, button)); } } } // Properties // /** * Indicates whether the {@code reorderable} option is set * * @return {@code false} by default */ public boolean isColumnReorderEnabled() { Boolean value = this.options.get("reorderable"); return value != null && value; } /** * Gets the row count * * @return the row count */ protected abstract long getRowCount(); /** * Gets the data-provider behavior's url * * @return the data-provider behavior's url */ protected abstract CharSequence getProviderUrl(); /** * Indicates whether the read function should use cache * * @return false by default * @see configuration-transport.read.cache */ protected boolean useCache() { return false; } /** * Gets the {@code List} of {@link ToolbarButton}{@code s} * * @return the {@code List} of {@code ToolbarButton}{@code s} */ protected List getToolbarButtons() { return Collections.emptyList(); } /** * Gets the {@code List} of visible {@link ToolbarButton}{@code s} * * @return the {@code List} of visible {@code ToolbarButton}{@code s} */ protected List getVisibleToolbarButtons() { List buttons = Generics.newArrayList(); for (ToolbarButton button : this.getToolbarButtons()) { if (button.isVisible()) { buttons.add(button); } } return buttons; } /** * Indicates whether toolbar buttons are supplied.
* Note: if false, the {@code toolbar} option can be used (for built-in buttons only) * * @return {@code true} or {@code false} */ protected boolean hasVisibleToolbarButtons() { return !this.getVisibleToolbarButtons().isEmpty(); } /** * Indicates whether the columns {@link IModel} contains a {@link CheckboxColumn} * * @return {@code true} if a {@link CheckboxColumn} is found */ protected boolean hasCheckboxColumn() { return this.columns.getObject().stream().anyMatch(column -> column instanceof CheckboxColumn); } /** * Gets the {@link List} of {@link CommandButton} * * @return the {@link List} of {@link CommandButton} */ protected List getCommandButtons() { List buttons = Generics.newArrayList(); for (IColumn column : this.columns.getObject()) { if (column instanceof CommandColumn) { buttons.addAll(((CommandColumn) column).getButtons()); } } return buttons; } /** * Gets the {@code List} of visible {@link CommandButton}{@code s} * * @return the {@code List} of visible {@code CommandButton}{@code s} */ protected List getVisibleCommandButtons() { List buttons = Generics.newArrayList(); for (CommandButton button : this.getCommandButtons()) { if (button.isVisible()) { buttons.add(button); } } return buttons; } /** * Gets the {@code List} of visible {@link CommandButton}{@code s} as json string * * @param column the {@code CommandColumn}, containing the {@code CommandButton}{@code s} * @param behaviors the {@code List} of {@code CommandAjaxBehavior}{@code s} which may be bound to buttons * @return the {@code List} of visible {@code CommandButton} as json string */ private static List getCommandButtonsAsString(CommandColumn column, List behaviors) { List list = Generics.newArrayList(); for (CommandButton button : column.getButtons()) { if (button.isVisible()) { JQueryAjaxBehavior behavior = getCommandAjaxBehavior(button, behaviors); list.add(button.toString(behavior)); } } return list; } /** * Gets the {@link CommandAjaxBehavior} associated to the {@link CommandButton}, if any * * @param behaviors the {@code List} of {@code CommandAjaxBehavior} * @param button the {@code CommandButton} * @return {@code null} if no {@code CommandAjaxBehavior} is associated to the button */ public static JQueryAjaxBehavior getCommandAjaxBehavior(CommandButton button, List behaviors) { for (CommandAjaxBehavior behavior : behaviors) { if (button.equals(behavior.getButton())) { return behavior; } } return null; } /** * Gets the {@code List} of {@link IColumn}{@code s} as json string * * @param columns the {@code List} of {@link IColumn}{@code s} * @param behaviors the {@code List} of {@link CommandAjaxBehavior}{@code s} associated to {@link CommandButton}{@code s} * @return the JSON string */ private static String getColumnsAsString(List columns, List behaviors) { // TODO replace StringBuilder StringBuilder builder = new StringBuilder("[ "); for (int i = 0; i < columns.size(); i++) { IColumn column = columns.get(i); if (i > 0) { builder.append(", "); } builder.append("{ "); builder.append(column.toString()); if (column instanceof CommandColumn) { // buttons // builder.append(", "); builder.append(Options.QUOTE).append("command").append(Options.QUOTE).append(": "); builder.append(getCommandButtonsAsString((CommandColumn) column, behaviors)); } builder.append(" }"); } return builder.append(" ]").toString(); } /** * Gets the 'read' callback function
* As create, update and destroy need to be supplied as function, we should declare read as a function as well. Weird... * * @return the 'read' callback function */ private String getReadCallbackFunction() { return KendoDataSource.getReadCallbackFunction(this.getProviderUrl(), this.useCache()); } // Events // @Override public void onConfigure(Component component) { final List columns = this.columns.getObject(); // this.setOption("edit", this.onEditAjaxBehavior.getCallbackFunction()); this.setOption("cancel", this.onCancelAjaxBehavior.getCallbackFunction()); if (this.onChangeAjaxBehavior != null) { this.setOption("change", this.onChangeAjaxBehavior.getCallbackFunction()); } if (this.onCheckedAjaxBehavior != null) { this.setOption("change", this.onCheckedAjaxBehavior.getCallbackFunction()); } if (this.onColumnReorderAjaxBehavior != null) { this.setOption("columnReorder", this.onColumnReorderAjaxBehavior.getCallbackFunction()); } // toolbar // if (this.hasVisibleToolbarButtons()) { this.setOption("toolbar", this.getVisibleToolbarButtons()); } // columns (+ column buttons) // this.setOption("columns", getColumnsAsString(columns, component.getBehaviors(CommandAjaxBehavior.class))); // schema // Options schema = new Options(); schema.set("data", Options.asString("results")); schema.set("total", Options.asString("__count")); schema.set("model", this.newSchemaModelOptions(columns)); // data-source // this.setOption("dataSource", this.dataSource.getName()); this.dataSource.set("schema", schema); this.dataSource.set("pageSize", this.getRowCount()); this.dataSource.set("serverPaging", true); this.dataSource.set("serverSorting", true); this.dataSource.set("serverFiltering", true); this.dataSource.setTransportRead(this.getReadCallbackFunction()); this.dataSource.setTransportCreate(this.onCreateAjaxBehavior.getCallbackFunction()); this.dataSource.setTransportUpdate(this.onUpdateAjaxBehavior.getCallbackFunction()); this.dataSource.setTransportDelete(this.onDeleteAjaxBehavior.getCallbackFunction()); this.onConfigure(this.dataSource); // last chance to set options // ajax // for (ToolbarAjaxBehavior behavior : component.getBehaviors(ToolbarAjaxBehavior.class)) { String selector = String.format("%s .k-grid-toolbar .k-grid-%s", this.getSelector(), behavior.getButtonName()); this.off(selector, "click"); this.on(selector, "click", behavior.getCallbackFunction()); } super.onConfigure(component); } /** * Configure the {@link KendoDataSource} with additional options * * @param dataSource the {@link KendoDataSource} */ protected void onConfigure(KendoDataSource dataSource) { // noop } @Override public void onAjax(AjaxRequestTarget target, JQueryEvent event) { if (event instanceof ToolbarClickEvent) { ToolbarClickEvent e = (ToolbarClickEvent) event; e.getButton().onClick(target, e.getValues()); this.listener.onClick(target, e.getButton(), e.getValues()); } if (event instanceof CommandClickEvent) { CommandClickEvent e = (CommandClickEvent) event; e.getButton().onClick(target, e.getValue()); this.listener.onClick(target, e.getButton(), e.getValue()); } if (event instanceof CancelEvent) { this.listener.onCancel(target); } if (event instanceof ChangeEvent) { this.listener.onChange(target, ((ChangeEvent) event).getItems()); } if (event instanceof CheckboxEvent) { this.listener.onChecked(target, ((CheckboxEvent) event).getSelectedKeys()); } if (event instanceof ColumnReorderEvent) { ColumnReorderEvent e = (ColumnReorderEvent) event; this.listener.onColumnReorder(target, e.getOldIndex(), e.getNewIndex(), e.getObject()); } if (event instanceof CreateEvent) { this.listener.onCreate(target, ((DataSourceEvent) event).getObject()); } if (event instanceof UpdateEvent) { this.listener.onUpdate(target, ((DataSourceEvent) event).getObject()); } if (event instanceof DeleteEvent) { this.listener.onDelete(target, ((DataSourceEvent) event).getObject()); } } // Factories // /** * Get the JSON model of the datasource's schema * * @param columns the {@code List} of {@link IColumn}{@code s} * @return the model, as JSON object */ protected Options newSchemaModelOptions(List columns) { Options model = new Options(); Options fields = new Options(); for (IColumn column : columns) { if (column.getField() != null) { if (column instanceof IdPropertyColumn) { model.set("id", Options.asString(column.getField())); } Options field = new Options(); if (column.isEditable() != null) { field.set("editable", column.isEditable()); } if (column.isNullable() != null) { field.set("nullable", column.isNullable()); } if (column.getType() != null) { field.set("type", Options.asString(column.getType())); } fields.set(column.getField(), field); } } model.set("fields", fields); return model; } /** * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'update' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@code JQueryAjaxBehavior} by default */ protected JQueryAjaxBehavior newOnCancelAjaxBehavior(IJQueryAjaxAware source) { return new JQueryAjaxBehavior(source) { private static final long serialVersionUID = 1L; @Override protected CallbackParameter[] getCallbackParameters() { return new CallbackParameter[] { CallbackParameter.context("e") }; } @Override protected JQueryEvent newEvent() { return new CancelEvent(); } }; } /** * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'change' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@link OnChangeAjaxBehavior} */ protected JQueryAjaxBehavior newOnChangeAjaxBehavior(IJQueryAjaxAware source) { return new OnChangeAjaxBehavior(source); } /** * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'change' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@link OnCheckedAjaxBehavior} */ protected JQueryAjaxBehavior newOnCheckedAjaxBehavior(IJQueryAjaxAware source) { return new OnCheckedAjaxBehavior(source); } /** * Gets a new {@link JQueryAjaxBehavior} that will be wired to the 'columnReorder' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@code JQueryAjaxBehavior} by default */ protected JQueryAjaxBehavior newColumnReorderAjaxBehavior(IJQueryAjaxAware source) { return new JQueryAjaxBehavior(source) { private static final long serialVersionUID = 1L; @Override protected CallbackParameter[] getCallbackParameters() { return new CallbackParameter[] { CallbackParameter.context("e"), // lf CallbackParameter.resolved("oldIndex", "e.oldIndex"), // lf CallbackParameter.resolved("newIndex", "e.newIndex"), // lf CallbackParameter.resolved("data", "kendo.stringify(e.column)") }; } @Override protected JQueryEvent newEvent() { return new ColumnReorderEvent(); } }; } /** * Gets a new {@link DataSourceAjaxBehavior} that will be wired to the datasource's 'create' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@code DataSourceAjaxBehavior} */ protected DataSourceAjaxBehavior newOnCreateAjaxBehavior(IJQueryAjaxAware source) { return new DataSourceAjaxBehavior(source) { // NOSONAR private static final long serialVersionUID = 1L; @Override protected JQueryEvent newEvent() { return new CreateEvent(); } }; } /** * Gets a new {@link DataSourceAjaxBehavior} that will be wired to the datasource's 'update' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@code DataSourceAjaxBehavior} */ protected DataSourceAjaxBehavior newOnUpdateAjaxBehavior(IJQueryAjaxAware source) { return new DataSourceAjaxBehavior(source) { // NOSONAR private static final long serialVersionUID = 1L; @Override protected JQueryEvent newEvent() { return new UpdateEvent(); } }; } /** * Gets a new {@link DataSourceAjaxBehavior} that will be wired to the datasource's 'delete' event * * @param source the {@link IJQueryAjaxAware} * @return a new {@code DataSourceAjaxBehavior} */ protected DataSourceAjaxBehavior newOnDeleteAjaxBehavior(IJQueryAjaxAware source) { return new DataSourceAjaxBehavior(source) { // NOSONAR private static final long serialVersionUID = 1L; @Override protected JQueryEvent newEvent() { return new DeleteEvent(); } }; } /** * Gets the {@link JQueryAjaxBehavior} that will be called when the user clicks a toolbar button * * @param source the {@link IJQueryAjaxAware} * @param button the button that is passed to the behavior so it can be retrieved via the {@link ToolbarClickEvent} * @return the {@link JQueryAjaxBehavior} */ protected JQueryAjaxBehavior newToolbarAjaxBehavior(DataTableBehavior source, final ToolbarButton button) { return new ToolbarAjaxBehavior(source, button); } /** * Gets a new {@link JQueryAjaxBehavior} that will be called by a table's button. This method may be overridden to provide additional behaviors * * @param source the {@link IJQueryAjaxAware} * @param button the button that is passed to the behavior so it can be retrieved via the {@link CommandClickEvent} * @return the {@link JQueryAjaxBehavior} */ protected abstract JQueryAjaxBehavior newCommandAjaxBehavior(IJQueryAjaxAware source, CommandButton button); // Ajax classes // /** * Provides a {@link JQueryAjaxBehavior} that aims to be wired to the 'change' event */ protected static class OnChangeAjaxBehavior extends JQueryAjaxBehavior { private static final long serialVersionUID = 1L; public OnChangeAjaxBehavior(IJQueryAjaxAware source) { super(source); } @Override protected CallbackParameter[] getCallbackParameters() { return new CallbackParameter[] { CallbackParameter.context("e"), CallbackParameter.resolved("items", "items") }; } @Override public CharSequence getCallbackFunctionBody(CallbackParameter... parameters) { String statement = ""; statement += "var $grid = e.sender;\n"; statement += "var _rows = jQuery.map(this.select(), function(row) { return $grid.dataItem(row); });\n"; // TODO REMOVE statement += "var items = kendo.stringify(_rows);\n"; return statement + super.getCallbackFunctionBody(parameters); } @Override protected JQueryEvent newEvent() { return new ChangeEvent(); } } /** * Provides a {@link JQueryAjaxBehavior} that aims to be wired to the 'change' event (for checkboxes) */ protected static class OnCheckedAjaxBehavior extends JQueryAjaxBehavior { private static final long serialVersionUID = 1L; /** * Constructor * * @param source the {@link Behavior} that will broadcast the event. */ public OnCheckedAjaxBehavior(final IJQueryAjaxAware source) { super(source); } @Override protected CallbackParameter[] getCallbackParameters() { return new CallbackParameter[] { CallbackParameter.context("e"), CallbackParameter.resolved("values", "this.selectedKeyNames()") }; } @Override protected JQueryEvent newEvent() { return new CheckboxEvent(); } } // Event object // /** * Provides an event object that will be broadcasted by the {@link #newOnCancelAjaxBehavior} callback */ protected static class CancelEvent extends JQueryEvent { } /** * Provides an event object that will be broadcasted by the {@link OnCheckedAjaxBehavior} callback */ protected static class CheckboxEvent extends JQueryEvent { private final List selectedKeys; public CheckboxEvent() { this.selectedKeys = ListUtils.toStringList(RequestCycleUtils.getQueryParameterValue("values")); } public List getSelectedKeys() { return selectedKeys; } } /** * Provides an event object that will be broadcasted by the {@linkplain #newColumnReorderAjaxBehavior} callback */ protected static class ColumnReorderEvent extends JQueryEvent { private final int oldIndex; private final int newIndex; private final JSONObject object; public ColumnReorderEvent() { this.oldIndex = RequestCycleUtils.getQueryParameterValue("oldIndex").toInt(0); this.newIndex = RequestCycleUtils.getQueryParameterValue("newIndex").toInt(0); String data = RequestCycleUtils.getQueryParameterValue("data").toString(); this.object = new JSONObject(data); } public int getOldIndex() { return this.oldIndex; } public int getNewIndex() { return this.newIndex; } public JSONObject getObject() { return this.object; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy