Storing state using 2 instances of the same JQuery plugin - javascript

I'm coding a jQuery plugin to build KendoUI grids. Everything was fine until I had to use 2 instances of the plugin in the same page. In this case, I need each instance of the plugin to have its own data.
I've read the jQuery guidelines for storing data with .data(), but when trying to access from outside the code of the plugin to the 'getSelectedItem()' this way:
selectedItemGrid1= $("#Grid1").kendoGrid_extended.getSelectedItem();
selectedItemGrid2= $("#Grid2").kendoGrid_extended.getSelectedItem();
I get the selectedItem of Grid2 in selectedItemGrid1. Here is a simplified version of my plugin code. Basically what I wanted to do is, anytime a row of the grid is selected ("change" event on the kendoGrid definition), store the new selected row inside the plugin, so when the user clicks on a "delete" button, read from the plugin the selected row and call the delete action with the info recovered from the plugin.
$.fn.kendoGrid_extended = function (options) {
var opts = $.extend({}, $.fn.kendoGrid_extended.defaults, options);
opts.controlId = '#' + this.attr('id');
var gridExtended;
var selectedItem;
var instance = $(this);
//Public accessor for the selectedItem object.
$.fn.kendoGrid_extended.getSelectedItem = function () {
return instance.data('selectedItem');
}
opts.gridDataSource = new kendo.data.DataSource({
type: "json",
serverPaging: true,
serverSorting: true,
sort: sortObject,
serverFiltering: true,
allowUnsort: true,
pageSize: opts.pageSize,
group: opts.group,
transport: {
read: {
url: opts.dataSourceURL,
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: function () { return opts.dataSourceParamsFunction(); }
},
parameterMap: function (options) {
return JSON.stringify(options);
}
},
schema: opts.dataSourceSchema,
});
gridExtended = $(opts.controlId).kendoGrid({
columns: opts.gridColumns,
dataSource: opts.gridDataSource,
pageable: {
refresh: true,
pageSizes: true
},
groupable: opts.groupable,
sortable: { mode: "multiple" },
filterable: false,
selectable: true,
scrollable: true,
resizable: true,
reorderable: true,
columnReorder: SetColumnsReorder,
columnResize: SetColumnsResize,
change: function () {
var gridChange = this;
gridChange.select().each(function () {
selectedItem = gridChange.dataItem($(this));
instance.data('selectedItem', selectedItem);
});
}
});
}
The code itself it's a simplified version of my plugin. I know that this may not be the best way to write a plugin as I've read the jQuery guidelines for plugin development. It would be awesome if you could point me in the right direction or tell me why my code is not working. Thanks a lot in advance!

I think you do not need that "public accessor" that you wrote, I think you actually can get it like this:
selectedItemGrid1= $("#Grid1").data('selectedItem);
selectedItemGrid2= $("#Grid2").data('selectedItem);
On a side you do not have to wrap that instance element into a jQuery object. You still be able to use rest of the jQuery methods, check the example.

I finally solved it appending a hidden input with a unique Id to the grid of the helper. On that hidden input I'm storing all the data that I want to be persistent with the Jquery .data().
So if I generate 2 grids using the plugin, each grid will have appended something like this:
<input type="hidden" id="uniqueIdHere" />

Related

Ext.Defer gives getAsynchronousLoad Error

I've just defined a combobox. Firstly it loads a countrylist and when select a value it's fire a change event which doing a ajax query to DB within searching service;
The thing; this configuration works pretty well when I click and open combobox items. But when I'm typing to combobox's field it's fires listener's store.load and because of none of country selected yet, the search query url gives not found errors of course.
{
xtype: 'countrycombo',
itemId: 'countryName',
name:'country',
afterLabelTextTpl: MyApp.Globals.required,
allowBlank: false,
flex: 1,
// forceSelection: false,
// typeAhead: true,
// typeAheadDelay: 50,
store: {
proxy: {
type: 'ajax',
// isSynchronous: true,
url: MyApp.Globals.getUrl() + '/country/list?limit=250',
// timeout: 300000,
reader: {
type: 'json',
rootProperty: 'data'
}
},
pageSize: 0,
sorters: 'description',
autoLoad: true
}
,
listeners: {
change: function (combo, countryId) {
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy()
.setUrl(MyAppp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
// Ext.defer(cityStore.load, 100);
cityStore.load();
}
}
},
I've tried several things as you see in code above to set a delay/timeout for load during typing to combobox text field; Ext.defer, timeoutconfig on proxy, typeAhead config on combo but none of them worked!
I thought that Ext.defer is the best solution but it gives this error:
Uncaught TypeError: me.getAsynchronousLoad is not a function at load (ProxyStore.js?_dc=15169)
How can I set a delay/timeout to combobox to fires load function?
Instead of Ext.defer(cityStore.load, 100);
try using this :
Ext.defer(function(){
cityStore.load
}, 300);
If this doest work, try increasing your delay
or you can put a logic before loading
like this :
if(countryId.length == 5){
cityStore.load
}
This will ensure that you Entered the right values before loading
Hope this helps, and Goodluck on your project
well.. I've tried to implement #Leroy's advice but somehow Ext.defer did not fire cityStore.load. So I keep examine similar situations on google and found Ext.util.DelayedTask
So configured the listerens's change to this and it's works pretty well;
listeners: {
change: function (combo, countryId) {
var alert = new Ext.util.DelayedTask(function () {
Ext.Msg.alert('Info!', 'Please select a country');
});
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy().setUrl(MyApp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
if (typeof countryId === 'number') {
cityStore.load();
} else {
alert.delay(8000);
}
}
}

Selectize: Setting Default Value in onInitialize with setValue

I have a web application with multiple Selectize objects initialized on the page. I'm trying to have each instance load a default value based on the query string when the page loads, where ?<obj.name>=<KeywordID>. All URL parameters have already been serialized are are a dictionary call that.urlParams.
I know there are other ways to initializing Selectize with a default value I could try; but, I'm curious why calling setValue inside onInitialize isn't working for me because I'm getting any error messages when I run this code.
I'm bundling all this JavaScript with Browserify, but I don't think that's contributing to this problem.
In terms of debugging, I've tried logging this to the console inside onInititalize and found that setValue is up one level in the Function.prototype property, the options property is full of data from load, the key for those objects inside options corresponds to the KeywordID. But when I log getValue(val) to the console, I get an empty string. Is there a way to make this work or am I ignoring something about Selectize or JavaScript?
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
onInitialize: function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
this.setValue(val);
},
load: function(query, callback) {
$.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
})
}
});
};
...
After sprinkling in some console.logs into Selectize.js, I found that the ajax data hadn't been imported, when the initialize event was triggered. I ended up finding a solution using jQuery.when() to make setValue fire after the data had been loaded, but I still wish I could find a one-function-does-one-thing solution.
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
load: function(query, callback) {
var self = this;
$.when( $.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
}) ).then(function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
self.setValue(val);
});
}
});
};
...
You just need to add the option before setting it as the value, as this line in addItem will be checking for it:
if (!self.options.hasOwnProperty(value)) return;
inside onInitialize you would do:
var val = that.urlParams[that.name]; //It might work with parseInt, I haven't used integers in selectize options though, only strings.
var opt = {id:val, text:val};
self.addOption(opt);
self.setValue(opt.id);
Instead of using onInitialize you could add a load trigger to the selectize. This will fire after the load has finished and will execute setValue() as expected.
var $select = $(this).container.selectize({
// ...
load: function(query, callback) {
// ...
}
});
var selectize = $select[0].selectize;
selectize.on('load', function(options) {
// ...
selectize.setValue(val);
});
Note that for this you first have to get the selectize instanze ($select[0].selectize).
in my case it need refresh i just added another command beside it
$select[0].selectize.setValue(opt);
i added this
$select[0].selectize.options[opt].selected = true;
and changes applied
but i dont know why?
You can initialize each selectize' selected value by setting the items property. Fetch the value from your querystring then add it as an item of the items property value:
const selectedValue = getQueryStringValue('name') //set your query string value here
$('#sel').selectize({
valueField: 'id',
labelField: 'title',
preload: true,
options: [
{ id: 0, title: 'Item 1' },
{ id: 1, title: 'Item 2' },
],
items: [ selectedValue ],
});
Since it accepts array, you can set multiple selected items

Kendo combobox takes time to load and open if datasource has a huge amount of data

I have tried using a Kendo Combobox with a datasource having a structure like:
{text: "12 Angry Men", value:"1"}
and the Kendo Combobox is initialised as:
$("#movies").kendoComboBox({
dataTextField: "text",
dataValueField: "value",
dataSource: data,
height: 100
})
.closest(".k-widget")
.attr("id", "movies_wrapper")
;
$("#filter").kendoDropDownList({
change: filterTypeOnChanged
});
It is found that for data of about 40,000 objects the combobox takes a 3 sec delay to load and 6-7 secs of delay to open it each time.
I debugged the native code and found out that it does take some time to handle the animation for so many objects to be shown in the popup.
I therefore tried passing animation as false.
But this still did not reduce the delay.Can someone help solve this problem?
I had a similar issue myself with trying to load approx. 20000 items into a combobox.
The way round it is to use server filtering to only pull back a limited number of results. I find 100 is a decent enough number and if the result isn't there then the filter term needs to be a bit more specific to get the eventual correct item.
It obviously means more trips to the server to get the results but I find that is trade off worth taking.
$('#' + fullControlID).kendoComboBox({
dataTextField: 'Text',
dataValueField: 'Value',
filter: "contains",
autoBind: true,
ignoreCase: true,
minLength: 3,
change: function (e) {
$('#' + fullControlID).data("kendoComboBox").dataSource.read();
},
dataSource: {
serverfiltering: true,
serverPaging: true,
pageSize: 100,
transport: {
read:
{
type: "POST",
dataType: "json",
url: transportUrl,
cache: false,
data: {
filterText: function () {
var filter = $('#' + fullControlID).data("kendoComboBox").input.val();
if(filter === '{the place holder text ignore it}')
{
filter = '';
}
return filter;
}
},
}
}
}
});
above is some code that I use to do this with the appropriate controller/action (using mvc) behind the scenes.
if you need more info then let me know.
Note: (fullcontrolID is the id for the control that is the combobox).

jQGrid celledit in JSON data shows URL Not set alert

I need to load a JSON from server and i want to enable a user to click and edit the value.
But when they edit, it should not call server. i mean i am not going to update immediately. So i dont want editurl. So i tried
'ClientArray' But still it shows Url is not set alert box. But i need
all the edited values when the user click Add Commented Items button this button will fire AddSelectedItemsToSummary() to save those in server
MVC HTML Script
<div>
<table id="persons-summary-grid"></table>
<input type="hidden" id="hdn-deptsk" value="2"/>
<button id="AddSelectedItems" onclick="AddSelectedItemsToSummary();" />
</div>
$(document).ready(function(){
showSummaryGrid(); //When the page loads it loads the persons for Dept
});
JSON Data
{"total":2,"page":1,"records":2,
"rows":[{"PersonSK":1,"Type":"Contract","Attribute":"Organization
Activity","Comment":"Good and helping og"},
{"PersonSK":2,"Type":"Permanant","Attribute":"Team Management",
"Comment":"Need to improve leadership skill"}
]}
jQGRID code
var localSummaryArray;
function showSummaryGrid(){
var summaryGrid = $("#persons-summary-grid");
// doing this because it is not firing second time using .trigger('reloadGrid')
summaryGrid.jqGrid('GridUnload');
var deptSk = $('#hdn-deptsk').val();
summaryGrid.jqGrid({
url: '/dept/GetPersonSummary',
datatype: "json",
mtype: "POST",
postData: { deptSK: deptSk },
colNames: [
'SK', 'Type', 'Field Name', 'Comments'],
colModel: [
{ name: 'PersonSK', index: 'PersonSK', hidden: true },
{ name: 'Type', index: 'Type', width: 100 },
{ name: 'Attribute', index: 'Attribute', width: 150 },
{ name: 'Comment', index: 'Comment', editable: true,
edittype: 'textarea', width: 200 }
],
cellEdit: true,
cellsubmit: 'clientArray',
editurl: 'clientArray',
rowNum: 1000,
rowList: [],
pgbuttons: false,
pgtext: null,
viewrecords: false,
emptyrecords: "No records to view",
gridview: true,
caption: 'dept person Summary',
height: '250',
jsonReader: {
repeatitems: false
},
loadComplete: function (data) {
localSummaryArray= data;
summaryGrid.setGridParam({ datatype: 'local' });
summaryGrid.setGridParam({ data: localSummaryArray});
}
});
)
Button click function
function AddSelectedItemsToSummary() {
//get all the items that has comments
//entered using cell edit and save only those.
// I need to prepare the array of items and send it to MVC controller method
// Also need to reload summary grid
}
Could any one help on this? why i am getting that URL is not set error?
EDIT:
This code is working after loadComplete changes. Before it was showing
No URL Set alert
I don't understand the problem with cell editing which you describe. Moreover you wrote "i need the edited value when the user click + icon in a row". Where is the "+" icon? Do you mean "trash.gif" icon? If you want to use cell editing, how you imagine it in case of clicking on the icon on the row? Which cell should start be editing on clicking "trash.gif" icon? You can start editing some other cell as the cell with "trash.gif" icon ising editCell method, but I don't think that it would be comfortable for the user because for the users point of view he will start editing of one cell on clicking of another cell. It seems me uncomfortable. Probably you want implement inline editing?
One clear error in your code is usage of showSummaryGrid inside of RemoveFromSummary. The function RemoveFromSummary create jqGrid and not just fill it. So one should call it only once. To refresh the body of the grid you should call $("#persons-summary-grid").trigger("refreshGrid"); instead. Instead of usage postData: { deptSK: deptSk } you should use
postData: { deptSK: function () { return $('#hdn-deptsk').val(); } }
In the case triggering of refreshGrid would be enough and it will send to the server the current value from the '#hdn-deptsk'. See the answer for more information.
UPDATED: I couldn't reproduce the problem which you described, but I prepared the demo which do what you need (if I understand your requirements correctly). The most important part of the code which you probably need you will find below
$("#AddSelectedItems").click(function () {
var savedRow = summaryGrid.jqGrid("getGridParam", "savedRow"),
$editedRows,
modifications = [];
if (savedRow && savedRow.length > 0) {
// save currently editing row if any exist
summaryGrid.jqGrid("saveCell", savedRow[0].id, savedRow[0].ic);
}
// now we find all rows where cells are edited
summaryGrid.find("tr.jqgrow:has(td.dirty-cell)").each(function () {
var id = this.id;
modifications.push({
PersonSK: id,
Comment: $(summaryGrid[0].rows[id].cells[2]).text() // 2 - column name of the column "Comment"
});
});
// here you can send modifications per ajax to the server and call
// reloadGrid inside of success callback of the ajax call
// we simulate it by usage alert
alert(JSON.stringify(modifications));
summaryGrid.jqGrid("setGridParam", {datatype: "json"}).trigger("reloadGrid");
});

reloading dataurl elements in jqGrid

I have a simple grid with the following options:
jQuery("#mygrid").jqGrid({
url: 'dataurl.htm',
datatype: 'json',
ajaxSelectOptions: { cache: false }
...
colModel: [
{ name: 'foo', index: 'foo', width: 25, stype: 'select', searchoptions:{ sopt:
['eq'], dataUrl: 'someurl.htm?columnName=foo'}}
]
});
However, when I call $("#mygrid").trigger("reloadGrid"); it only loads the data for the table from dataurl.htm but it does not load the data for the foo column from the some url.htm link.
I've read couple of questions like these on SO and it was suggested to have ajaxSelectOptions: { cache: false } but this is not working for me.
The someurl.htm returns <select> <option val=''>something</option></select>
It's absolutely correct question! The current implementation of jqGrid has only toggleToolbar method which can hide the toolbar, but the toolbar itself will be created once. So all properties of the toolbar stay unchanged the whole time.
To fix the problem I wrote small additional method destroyFilterToolbar which is pretty simple:
$.jgrid.extend({
destroyFilterToolbar: function () {
"use strict";
return this.each(function () {
if (!this.ftoolbar) {
return;
}
this.triggerToolbar = null;
this.clearToolbar = null;
this.toggleToolbar = null;
this.ftoolbar = false;
$(this.grid.hDiv).find("table thead tr.ui-search-toolbar").remove();
});
}
});
The demo uses the method. One can recreate searching toolbar after changing of properties of some columns. In the code below one can change the language of some texts from the searching toolbar:
The corresponding code is below:
$("#recreateSearchingToolbar").change(function () {
var language = $(this).val();
// destroy searching toolbar
$grid.jqGrid("destroyFilterToolbar");
// set some searching options
$grid.jqGrid("setColProp", "closed",
language === "de" ?
{searchoptions: {value: ":Beliebig;true:Ja;false:Nein"}} :
{searchoptions: {value: ":Any;true:Yes;false:No"}});
$grid.jqGrid("setColProp", "ship_via",
language === "de" ?
{searchoptions: {value: ":Beliebig;FE:FedEx;TN:TNT;IN:Intim"}} :
{searchoptions: {value: ":Any;FE:FedEx;TN:TNT;IN:Intim"}});
// create new searching toolbar with nes options
$grid.jqGrid("filterToolbar", {stringResult: true, defaultSearch: "cn"});
});

Categories