You can add static form object's like selects & inputs to datatable columns as shown here:
https://datatables.net/examples/api/form.html
But I'm trying to add custom bootstrap widgets. Like TouchSpin found here:
http://www.virtuosoft.eu/code/bootstrap-touchspin/
When I initialize TouchSpin it only initializes the objects on the first datatables page shown, the objects on the second/third pages are not initialized.
Anyone have an idea how to fix this? Its probably because the second/third pages are not part of the DOM yet?
What you need to hook into is the "drawCallback" function. You can specify it in the options when creating the DataTable. It would look something like this:
var table = $('#example').DataTable({
//... Your other options ... //
drawCallback: function(settings) {
// This callback is called when the draw is complete which happens on:
// paging, sorting, and of course when you call ".draw()". The current
// DataTable page is in the DOM with all the HTML.
// You can get the jquery element for the table like this.
var dataTableJqueryObj = $(this);
// You can get the API object like this.
var apiDataTableObj = this.api();
// Initialize your controls ...
}
});
Let me know if you have any other questions.
Related
When I construct a normal table and give each column a distinct class, the classes are responsive for all rows. However, when I call .dataTable() on my table, only page 1 of the paginated results is responsive. Page 2 and beyond are not responsive.
Example code:
var dataTableID = 'questionsTable';
var columns = {
questionID: "ID",
CategoryString: "Cat",
difficultyLevel: "Diff",
timesAsked: "Qty",
questionText: "Question Text"
};
// my own little function that builds the HTML table. <TD> classes are column names
//-- eg .questionID, .CategoryString, etc
var tableHTML = makeTable(questions, columns);
$('#' + dataTableID).html(tableHTML);
// dataTable works nicely except only page 1 .click is responsive!
$('#' + dataTableID).dataTable();
// works fine if I remove .dataTable() above. ONLY works for first page of results
// if I keep .dataTable()
$('.questionID').on("click", function() {
alert('Click called');
});
When using pagination, dataTables change the visible rows by moving <tr>'s back and forth the DOM. $('.questionID').on is processed for columns on the first page only, since those columns is the only visible (accessible) columns after initialization.
So you must assign the click handler (or whatever you want to attach) on page change rather than on initialization. Here by the use of the fnDrawCallback event :
function clickHandler() {
alert('Click called');
}
var dataTable = $('#example').dataTable({
fnDrawCallback : function() {
$('.questionID')
.off("click", clickHandler)
.on("click", clickHandler)
}
});
see demo -> http://jsfiddle.net/U9Jmg/
Notice the use of .off. dataTables actually moves the <tr>'s back and forth the DOM and a table in memory, including any attached events. If previous attached events is not released, we will end up in multiple alerts for each column.
Also note I have only used only one class .questionID for all the columns in the demo. The example is 1.10.x but this works in 1.9.x as well.
As you can see of the comments below, you could also use a delegated event instead of direct event binding. It changes the setup and perhaps it is not suitable for your needs, but anyway :
$('#example tbody').on('click', '.questionID', clickHandler);
see demo -> http://jsfiddle.net/L29Dq/
When using DataTables pagination feature (as you do), only the first page of your data is present in the dom when you attach the click event handler. That's why the handler is attached to the elements on the first page and only to those elements.
When going to another page, DataTables will redraw the table which in effect removes the attached event handler. You have to reattach the event handler after every table draw. The drawCallback option should be the right place for that:
$('#' + dataTableID).dataTable({
"drawCallback": function(settings){
$('.questionID').on("click", function(){
alert('Click called');
});
}
});
As #davidkonrad pointed out in his answer, the click handler should be removed (using off) to avoid handling the event multiple times.
The DataTables page has a section on this as well: Advanced Initialisation - DOM/jQuery events. The example there uses delegated events.
I had a List that used to work when it was bound directly to a store but now I want that list to get it's data from a queryBy on the original store.
Looking at the documentation is seems like setItems should do what I want.
var myStore = Ext.getStore('myStoreData');
var myData = myStore.queryBy(function(item) {
return item.get('status') !== null;
});
// At this point myData looks valid and has the data I want.
// Ext.apply.create.Class {all: Array[5], items: Array[5], keys: Array[5], indices: Object, map: Object…}
Ext.getCmp('myListComponent').setItems(myData.items);
I keep getting the error "Object [object Object] has no method 'getItemId'". I tried various other incantations but without success. I also took a look at setData and add but without success.
========================
After getting Thiem's answer I just ended up creating a function that would create a filtered copy of an existing store and then just setting the List store to that. Code below for others edification...
storeCopy: function(store, filterBy) {
var records = [];
var allRecords = null;
if(filterBy)
allRecords= store.queryBy(filterBy);
else
allRecords= store.queryBy(function(){return true;});
allRecords.each(function(r){
var rec = r.copy();
rec.setId(r.getId());
records.push(rec);
});
var store2 = new Ext.data.Store({
recordType: store.recordType
});
store2.add(records);
return store2;
},
Thanks all.
setItems method does a totally different thing. For example, says you have an Ext.Container which consists of a form, some fields, and some interaction buttons. These things are call child components, or items of the container. They are oftenly declared in the items config of the parent container and setItems is designed to programmatically set the value of that config. So it has nothing to do with the store logic.
In your situation, here is one of the solutions:
Create a store instance which contains filtered data.
Use this command: yourList.setStore('yourFilteredStore')
And it should reload... hope this helps
I am using extjs 4.1.1a for developing some application.
I had a form consisting of two combo-boxes and an item-selector.
Based on the value selected in first combo-box , the itemselector will load its data from database. This is working fine.
My problem is, if i reselect the first combo-box the new data will be displayed in itemselector along with previous data displayed in itemseletor .That is previous data displayed in itemselector will remain there itself.
for example: name "test1" consists of ids 801,2088,5000. on selecting test1 in firstcombobox itemselector must show output as below.
and if "test2" consists of ids 6090,5040. on selecting test2 in firstcombobox itemselector must show output as below.
problem is. for first time if i select "test1" from firstcombobox , output will come as expected. if i reselect "test2" from firstcombobox , output will come as below.
as you can see, previous data displayed (marked in red rectagle) remains there itself with new data displayed (marked with green rectangle).
I want for every reselection of first combobox, previously displayed data in itemselector to be erased before printing new data on itemselector.
How can I reset the itemselector for every reselection of first combobox?
You should remove all items from the store of the itemselector by the removeAll command. After that you should load the store of the itemselector.
itemselector.store.removeAll();
itemselector.store.load();
Any solutions above solve my problem.
i found solution from Sencha Forum.
https://www.sencha.com/forum/showthread.php?142276-closed-Ext-JS-4.0.2a-itemselector-not-reloading-the-store
in the itemselector.js file, change the line marked below.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
// THIS LINE BELOW MUST BE CHANGED!!!!!!!!!!!!
fromStore.loadData(store.getRange()); //fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
You need to insert...
this.reset();
at the head of the function that is inserting the data.
As an example...
Ext.override( Ext.ux.ItemSelector, {
setValue: function(val) {
this.reset();
if (!val) return;
val = val instanceof Array ? val : val.split(this.delimiter);
var rec, i, id;
for (i = 0; i < val.length; i++) {
var vf = this.fromMultiselect.valueField;
id = val[i];
idx = this.toMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
if (idx != -1) continue;
idx = this.fromMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
rec = this.fromMultiselect.view.store.getAt(idx);
if (rec) {
this.toMultiselect.view.store.add(rec);
this.fromMultiselect.view.store.remove(rec);
}
}
}
});
are u got it?
when u select that combobox frist stoe of item selector is null after store load with ur pass the para meters
for example
store.load(null),
store.proxey.url='jso.php?id='+combobox.getrawvalue(),
store.load();
like that so when ur select a value in ur combobox that time ur used a listeners to ur combobox in that listners ur used above code , select ur some value in combobox that time frist store is get null after ur pass some values to json.php then store load with responce so that time old data is remove and new data load in that store
if u post ur code i will give correct code
I ran into the same issue with ExtJS 4.2.1. I got it to work by calling reload() on the data store and then setValue() with an empty string on the item selector in the data store's reload() callback.
Ext.create("Ext.form.field.ComboBox", {
// Other properties removed for brevity
listeners: {
change: function(field, newValue, oldValue, eOpts) {
Ext.getStore("ExampleStore").reload({
callback: function() {
Ext.getCmp("ExampleItemSelector").setValue("");
}
});
}
}
});
Ext.create("Ext.data.Store", {
storeId: "ExampleStore",
// Other properties removed for brevity
});
Ext.create("Ext.form.FormPanel", {
// Other properties removed for brevity
items:[{
xtype: "itemselector",
id: "ExampleItemSelector",
// Other properties removed for brevity
}]
});
For any folks that are curious, I'm fairly convinced there's a bug in the item selector's populateFromStore() function. When the function is called, it blindly adds all of the values from the bound store (store) to the internal store (fromStore). I suspect there should be a call to fromStore.removeAll() prior to the call to fromStore.add(). Here's the relevant code from ItemSelector.js.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
EDIT 12/18/2013
If you've configured any callback events on the item selector (e.g. change), you may want to disable the events temporarily when you call setValue(""). For example:
var selector = Ext.getCmp("ExampleItemSelector");
selector.suspendEvents();
selector.setValue("");
selector.resumeEvents();
I had the same problem and finally I decided to modify the extjs source code, not considering it a big issue as extjs itself its saying in the start of the file
Note that this control will most likely remain as an example, and not as a core Ext form
control. However, the API will be changing in a future release and so should not yet be
treated as a final, stable API at this time.
Based on that, as jstricker guessed (and sadly I didn't read and took me a while to arrive to the same conclusion), adding fromStore.removeAll() before fromStore.add() solves the problem.
Outside of the problem (but I think it can be interesting as well), additionally, I also added listConfig: me.listConfig in the MultiSelect configuration (inside createList), that way it's possible to format each item additional options (such as images, etc.) setting in the 'itemselector' the option listConfig as it's explained in the (irrealistic) documentation.
Need to reset the store used in ItemSelector that can be done by setting Empty object like below. Also need to call clearValue() method of ItemSelector component.
store.setData({});
ItemSelectorComponent.clearValue();
How can I receive all events attached to an element with dojo?
dojo.query('#mydiv') // which events does #mydiv has?
To get all events on a DOM element:
// Get my div
myDiv = dojo.byId("myDiv");
// Obtain all event-related attributes
var events = dojo.filter(
myDiv.attributes,
function(item) {
return item.name.substr(0, 2) == 'on';
}
);
// Execute first found event, just for fun
eval(events[0].value);
If you get myDiv using dojo.query, remember that dojo.query returns an array, so your element would be in myDiv[0].
This solution does not work with events attached with dojo.connect. There probably is a way to extract this info from Dojo inner workings, but you would have to delve into the source code to understand how.
Another option is that you explicitly manage all dojo.connect events with a global registry. You could use dojox.collections to make this easier. For example, creating a global registry whose keys will be the dom nodes, and values will be the handles returned by dojo.connect (these handles contain the dom node, the type of event and the function to execute):
// On startup
dojo.require(dojox.collections.Dictionary);
eventRegistry = new dojox.collections.Dictionary();
...
// Registering an event for dom node with id=myDiv
var handle1 = dojo.connect(dojo.byId("myDiv"), "onclick", null, "clickHandler");
// Check if event container (e.g. an array) for this dom node is already created
var domNode = handle1[0];
if (!eventRegistry.containsKey(domNode))
eventRegistry.add(domNode, new Array());
eventRegistry.item(domNode).push(handle1);
...
// Add another event later to myDiv, assume container (array) is already created
var handle2 = dojo.connect(dojo.byId("myDiv"), "onmouseover", null, "mouseHandler");
eventRegistry.item(domNode).push(handle2);
...
// Later get all events attached to myDiv, and print event names
allEvents = eventRegistry.item(domNode);
dojo.forEach(
allEvents,
function(item) {
console.log(item[1]);
// Item is the handler returned by dojo.connect, item[1] is the name of the event!
}
);
You can hide the annoying check to see if event container is already created by creating a subclass of dojox.collections.Dictionary with this check already incorporated. Create a js file with this path fakenmc/EventRegistry.js, and put it beside dojo, dojox, etc:
dojo.provide('fakenmc.EventRegistry');
dojo.require('dojox.collections.Dictionary');
dojo.declare('fakenmc.EventRegistry', dojox.collections.Dictionary, {
addEventToNode : function(djConnHandle) {
domNode = djConnHandle[0];
if (!this.containsKey(domNode))
this.add(domNode, new Array());
this.item(domNode).push(djConnHandle);
}
});
Using the above class you would have to dojo.require('fakenmc.EventRegistry') instead of 'dojox.collections.Dictionary', and would simply directly add the dojo connect handle without other checks:
dojo.provide('fakenmc.EventRegistry');
eventRegistry = new fakenmc.EventRegistry();
var handle = dojo.connect(dojo.byId("myDiv"), "onclick", null, "clickHandler");
eventRegistry.addEventToNode(handle);
...
// Get all events attached to node
var allEvents = eventRegistry.item(dojo.byId("myDiv"));
...
This code is not tested, but I think you get the idea.
If its only for debugging purpose. You can try dijit.byId("myId").onClick.toString(); in your firebug console and you can see the entire onclick code this works even if the function is anonymous you can view the content of anonymous content.
I have an ExtJS grid that has a button set up in it. The button triggers a function that's defined into other JS file that's included in the grid page. The function triggers ok but in that function I want to get the columns count like this:
grid.getColumnModel().getColumnCount()
The problem is that I get an error like: grid.getColumnModel is not a function.
In PHP I would make a "global $ext" and then access that function. How can I do this in Ext ? How can I access the grid from other file ? What needs to be defined ?
Thank you.
How did you define the grid object? Did you do it like this:
var grid = new Ext.grid.GridPanel(...);
If so, the grid object is not in global scope. Remove the "var" and see if it helps.
This looks like a scope issue. See variable scope in JavaScript.
Basically, you can do:
my_global_grid = ... // accessible in the current ~global~ context (document, window)
var my_local_grid = ... // accessible only in the function
window.my_window_global_grid = ... // accessible in the same window
You might also pass the grid object into your function as an argument:
function myFunction(arg1,arg2,grid){
...
var count = grid.getColumnModel().getColumnCount();
...
}