When using Datatables (Ver: 1.10.16), I noticed that the data in the API is not updated immediately via ajax.reload in the callback even though the site says the callback is not called until the new data has arrived and been redrawn.
Notes up front:
All the data is formatted correctly and displays in the table before and after the ajax.reload, including the new data from the reload.
If I click reload twice, the api sees the new data properly and ApplyHeaderFilters works properly.
When I say the API seeing the data properly I mean like so:
$('#dtTbl').DataTable().column('1:visible').data().unique()
The ApplyHeaderFilters is the callback on ajax.reload and uses the above JS command to get unique values from the column. The data returned from the JS command are not reflecting the new data that is returned from the reload.
This is in the Document Ready:
batchDT = $('#dtTbl').DataTable( {
deferLoading: true,
pageLength: 25,
pagingType: 'simple_numbers',
scrollx: true,
initComplete: function () {
ApplyHeaderFilters($(this).attr('id'), this.api());
},
ajax: {
url: "mysite.cfm?method=gettabledata",
type: 'POST'
},
columns: [
{ title: "Description", name: "description", data: "description"},
{ title: "Is Active", name: "isactive", data: "isactive"},
{ title: "List Item ID", name: "listitemid", data: "listitemid"},
{ title: "Name", name: "name", data: "name"},
{ title: "Table Ref ID", name: "tablerefid", data: "tablerefid", orderable: false}
]
} );
$("#reload").on('click',function(){
batchDT.ajax.reload(ApplyHeaderFilters('dtTbl', $('#dtTbl').DataTable()));
});
For some reason the callback was being called before the reload was completed. I fixed this by wrapping my callback function in reload in an anon function. If anyone has ideas why this would be this way comment please. I have a feeling it has something to do with closures and how they are handling the callback in the datatables library.
$("#reload").on('click',function(){
batchDT.ajax.reload(function(){
ApplyHeaderFilters('dtTbl', $('#dtTbl').DataTable());
});
});
Related
I want to draw a table to show users data from my server.
First I am using Ajex to get the users data:
var usersList = {};
usersList.users = ["Afthieleanmah", "Hadulmahsanran","tabletest1"];
var dataSet1=[];
var i;
$.ajax({
url: '../users',
type: 'POST',
contentType: 'application/json',
cache: false,
data: JSON.stringify(usersList),
success:function(response, text){
if(response.users !== undefined){
dataSet1 = response.users;
}
}
});
I can successfully get the users data and save the data in dataSet1 as a JSON array contains Objects. Its format is like this:
[
{
username: "Tiger Nixon",
job_title: "System Architect",
city: "Edinburgh",
extn: "5421"
},
{
username: "Tiger Nixon2",
job_title: "System Architect",
city: "Edinburgh",
extn: "5421"
}
]
Then I create a table and pass in configuration:
// table confirgurations
var tableConfig={
pageLength: 5,
bLengthChange: false,
columns:[
{data: "username", title: "Name"},
{data: "job_title", title: "Position"},
{data: "city", title: "City"}
],
data:dataSet1
};
// create table
var userTable=$('#table-id').DataTable(tableConfig);
I am sure that I can get users data from API "/users" and save it into dataSet1. But everytime I load the page containing the table, the table always shows "No data available in table". I set a breakpoint on this line :
var tableConfig={
and let it continue to run. The weird things happen. The Table shows the data.............. No idea why
You should initialize your table after you receive response from the server in the success function. Also use destroy in case you're performin your Ajax request multiple times.
For example:
$.ajax({
// ... skipped ...
success:function(response, text){
if(response.users !== undefined){
dataSet1 = response.users;
}
// table confirgurations
var tableConfig={
// ... skippped ...
destroy: true
};
// ... skippped ...
var userTable=$('#table-id').DataTable(tableConfig);
}
});
However ideally you should let jQuery DataTables do the Ajax request using ajax option.
I'm very new to Datatables plugin and i'm using it for my small project. I have the following problem like this:
+ I want to create a table and each row have a link to pop up a modal for editing.
Currently my datatables implementation as follow:
$(document).ready(function () {
$('#dtTable').DataTable({
serverSide: false,
processing: true,
deferRender: true,
ajax: {
type: 'POST',
url: '#Url.Action("GetClasses", "CLASSes")',
dataSrc: ""
},
columns: [
{ data: 'CLASSID' },
{ data: 'CLASSCODE' },
{ data: 'CLASSNAME' },
{
orderable: false,
searchable: false,
render: function (data, type, full, meta) {
debugger;
var data = full.CLASSID;
return 'Action';
}
}
]
});
})
The problem is that when ever i click on the Action link the modal will appear and then disappear instantly, place a debugger at the render section it's seemed that this section call twice and i don't know why?
So please help me to achieve this, each row has its link to pop up a modal and when click on it.
Thanks you guys very much
jQuery DataTables plug-in indeed calls render multiple times: for data type detection, display, sorting, etc.
Use the following code to produce content for display only:
render: function (data, type, full, meta) {
if(type === 'display'){
data = 'Action';
}
return data;
}
Regarding modal dialogs, most likely there is a problem somewhere else in your code that makes the modal dialog disappear.
I'm new to rally app SDK and trying to do the tutorials (from Youtube and from rally site)
when I'm trying to create an iterationComboBox the object is created but with no values ("There are no Iterations defined").
i tried to run both the video tutorial code from github (session_4_interactive_grid)
// Custom Rally App that displays Defects in a grid and filter by Iteration and/or Severity.
//
// Note: various console debugging messages intentionally kept in the code for learning purposes
Ext.define('CustomApp', {
extend: 'Rally.app.App', // The parent class manages the app 'lifecycle' and calls launch() when ready
componentCls: 'app', // CSS styles found in app.css
defectStore: undefined, // app level references to the store and grid for easy access in various methods
defectGrid: undefined,
// Entry Point to App
launch: function() {
console.log('our second app'); // see console api: https://developers.google.com/chrome-developer-tools/docs/console-api
this.pulldownContainer = Ext.create('Ext.container.Container', { // this container lets us control the layout of the pulldowns; they'll be added below
id: 'pulldown-container-id',
layout: {
type: 'hbox', // 'horizontal' layout
align: 'stretch'
}
});
this.add(this.pulldownContainer); // must add the pulldown container to the app to be part of the rendering lifecycle, even though it's empty at the moment
this._loadIterations();
},
// create iteration pulldown and load iterations
_loadIterations: function() {
this.iterComboBox = Ext.create('Rally.ui.combobox.IterationComboBox', {
fieldLabel: 'Iteration',
labelAlign: 'right',
width: 300,
listeners: {
ready: function(combobox) { // on ready: during initialization of the app, once Iterations are loaded, lets go get Defect Severities
this._loadSeverities();
},
select: function(combobox, records) { // on select: after the app has fully loaded, when the user 'select's an iteration, lets just relaod the data
this._loadData();
},
scope: this
}
});
this.pulldownContainer.add(this.iterComboBox); // add the iteration list to the pulldown container so it lays out horiz, not the app!
},
// create defect severity pulldown then load data
_loadSeverities: function() {
this.severityComboBox = Ext.create('Rally.ui.combobox.FieldValueComboBox', {
model: 'Defect',
field: 'Severity',
fieldLabel: 'Severity',
labelAlign: 'right',
listeners: {
ready: function(combobox) { // this is the last 'data' pulldown we're loading so both events go to just load the actual defect data
this._loadData();
},
select: function(combobox, records) {
this._loadData();
},
scope: this // <--- don't for get to pass the 'app' level scope into the combo box so the async event functions can call app-level func's!
}
});
this.pulldownContainer.add(this.severityComboBox); // add the severity list to the pulldown container so it lays out horiz, not the app!
},
// Get data from Rally
_loadData: function() {
var selectedIterRef = this.iterComboBox.getRecord().get('_ref'); // the _ref is unique, unlike the iteration name that can change; lets query on it instead!
var selectedSeverityValue = this.severityComboBox.getRecord().get('value'); // remember to console log the record to see the raw data and relize what you can pluck out
console.log('selected iter', selectedIterRef);
console.log('selected severity', selectedSeverityValue);
var myFilters = [ // in this format, these are AND'ed together; use Rally.data.wsapi.Filter to create programatic AND/OR constructs
{
property: 'Iteration',
operation: '=',
value: selectedIterRef
},
{
property: 'Severity',
operation: '=',
value: selectedSeverityValue
}
];
// if store exists, just load new data
if (this.defectStore) {
console.log('store exists');
this.defectStore.setFilter(myFilters);
this.defectStore.load();
// create store
} else {
console.log('creating store');
this.defectStore = Ext.create('Rally.data.wsapi.Store', { // create defectStore on the App (via this) so the code above can test for it's existence!
model: 'Defect',
autoLoad: true, // <----- Don't forget to set this to true! heh
filters: myFilters,
listeners: {
load: function(myStore, myData, success) {
console.log('got data!', myStore, myData);
if (!this.defectGrid) { // only create a grid if it does NOT already exist
this._createGrid(myStore); // if we did NOT pass scope:this below, this line would be incorrectly trying to call _createGrid() on the store which does not exist.
}
},
scope: this // This tells the wsapi data store to forward pass along the app-level context into ALL listener functions
},
fetch: ['FormattedID', 'Name', 'Severity', 'Iteration'] // Look in the WSAPI docs online to see all fields available!
});
}
},
// Create and Show a Grid of given defect
_createGrid: function(myDefectStore) {
this.defectGrid = Ext.create('Rally.ui.grid.Grid', {
store: myDefectStore,
columnCfgs: [ // Columns to display; must be the same names specified in the fetch: above in the wsapi data store
'FormattedID', 'Name', 'Severity', 'Iteration'
]
});
this.add(this.defectGrid); // add the grid Component to the app-level Container (by doing this.add, it uses the app container)
}
});
and the code from Rally site (https://help.rallydev.com/apps/2.0rc2/doc/#!/guide/first_app).
// Custom Rally App that displays Defects in a grid and filter by Iteration and/or Severity.
//
// Note: various console debugging messages intentionally kept in the code for learning purposes
Ext.define('CustomApp', {
extend: 'Rally.app.App', // The parent class manages the app 'lifecycle' and calls launch() when ready
componentCls: 'app', // CSS styles found in app.css
launch: function() {
this.iterationCombobox = this.add({
xtype: 'rallyiterationcombobox',
listeners: {
change: this._onIterationComboboxChanged,
ready: this._onIterationComboboxLoad,
scope: this
}
});
},
_onIterationComboboxLoad: function() {
var addNewConfig = {
xtype: 'rallyaddnew',
recordTypes: ['User Story', 'Defect'],
ignoredRequiredFields: ['Name', 'ScheduleState', 'Project'],
showAddWithDetails: false,
listeners: {
beforecreate: this._onBeforeCreate,
scope: this
}
};
this.addNew = this.add(addNewConfig);
var cardBoardConfig = {
xtype: 'rallycardboard',
types: ['Defect', 'User Story'],
attribute: 'ScheduleState',
storeConfig: {
filters: [this.iterationCombobox.getQueryFromSelected()]
}
};
this.cardBoard = this.add(cardBoardConfig);
},
_onBeforeCreate: function(addNewComponent, record) {
record.set('Iteration', this.iterationCombobox.getValue());
},
_onIterationComboboxChanged: function() {
var config = {
storeConfig: {
filters: [this.iterationCombobox.getQueryFromSelected()]
}
};
this.cardBoard.refresh(config);
}
});
both give me an empty iteration box.
i'm getting user stories data when running code from session 3 on the video,by creating a store of user stories. I googled it and searched here for duplicates but with no successso far, so what can be the issue?
Thanks!
I copied the code you posted, both apps, without making any changes, ran the apps and the iteration box was populated in both cases. It's not the code.
Maybe if you are getting "There are no Iterations defined" there are no iterations in your project?
The second code you posted which you copied from the example in the documentation has a bug in it and even though the iteration combobox is populated, the cards do not show on a board. DevTools console has error: "Cannot read property 'refresh' of undefined".
I have a working version of this app in this github repo.
I can't figure out why my data won't load to my AccordionList element from here:
https://github.com/kawanoshinobu/Ext.ux.AccordionList
I'm creating it within a panel like so:
{
xtype: 'accordionlist',
store: Ext.create('Rks.store.Bcks'),
flex: 1
},
It calls a store which is defined like so:
Ext.define('Rks.store.Bcks', {
extend: 'Ext.data.TreeStore',
requires: ['Rks.model.Bck'],
config: {
itemId: 'bks',
model: 'Rks.model.Bck',
defaultRootProperty: 'items',
proxy: {
type: 'ajax',
url: 'path/to/ajax',
},
autoLoad: false,
listeners:{
load: function( me, records, successful, operation, eOpts ){
console.log("data loaded", records);
}
}
}
});
When I call the view which contains the accordion, the console logs what appears to be a good object:
items: [{bck_id:3, author_id:1, title:test, items:[{c_id:2, bck_id:3, title:choice1, leaf:true}]}]
But nothing shows up. The panel is empty and no accordion items show.
However, when I replace the proxy with inline JSON, everything looks fine:
Ext.define('Rks.store.Bcks', {
extend: 'Ext.data.TreeStore',
requires: ['Rks.model.Bck'],
config: {
itemId: 'bks',
model: 'Rks.model.Bck',
defaultRootProperty: 'items',
root: {
items: [
{ bck_id: 1, author_id: 1, title: 'bck1', items: [ {c_id: 1, bck_id: 1, title: 'choice1', leaf: true} ] }
]
}
autoLoad: false,
listeners:{
load: function( me, records, successful, operation, eOpts ){
console.log("data loaded", records);
}
}
}
});
Here the items show up in the accordion. I can't figure out why the second example works and the first doesn't. Is there something special I should be doing when calling the store proxy for Accordion?
UPDATE: I have managed to get the accordion list to display data, but when I change the url of the store and reload it, the store reloads but the accordion list does not update. The accordion list continues to display the data it receives from the first URL, not from reloads with modified URLS.
Thanks
I think I figured this out. For the accordionlist component, you need to do like so:
var accordionlist = Ext.ComponentQuery.query('rdb #rdb1')[0];
var brickstore = Ext.getStore('bcs');
bcs.removeAll();
bcs.getProxy().setUrl('newurl');
accordionlist.setStore(bcs);
accordionlist.load();
basically, manually remove all items, set the new url, set the store on the list, then load the list.
I think there was a problem with your proxy configuration. First, remove the defaultRootProperty config (which is out of proxy config), then try this:
proxy: {
type: 'ajax',
url: (your ajax url),
reader: {
type: 'json',
rootProperty: 'items'
}
},
On the front-end I have a Calls grid. Each Call may have one or more Notes associated with it, so I want to add the ability to drill down into each Calls grid row and display related Notes.
On the back-end I am using Ruby on Rails, and the Calls controller returns a Calls json recordset, with nested Notes in each row. This is done using to_json(:include => blah), in case you're wondering.
So the question is: how do I add a sub-grid (or just a div) that gets displayed when a user double-clicks or expands a row in the parent grid? How do I bind nested Notes data to it?
I found some answers out there that got me part of the way where I needed to go. Thanks to those who helped me take it from there.
I'll jump straight into posting code, without much explanation. Just keep in mind that my json recordset has nested Notes records. On the client it means that each Calls record has a nested notesStore, which contains the related Notes. Also, I'm only displaying one Notes column - content - for simplicity.
Ext.define('MyApp.view.calls.Grid', {
alias: 'widget.callsgrid',
extend: 'Ext.grid.Panel',
...
initComponent: function(){
var me = this;
...
var config = {
...
listeners: {
afterrender: function (grid) {
me.getView().on('expandbody',
function (rowNode, record, expandbody) {
var targetId = 'CallsGridRow-' + record.get('id');
if (Ext.getCmp(targetId + "_grid") == null) {
var notesGrid = Ext.create('Ext.grid.Panel', {
forceFit: true,
renderTo: targetId,
id: targetId + "_grid",
store: record.notesStore,
columns: [
{ text: 'Note', dataIndex: 'content', flex: 0 }
]
});
rowNode.grid = notesGrid;
notesGrid.getEl().swallowEvent(['mouseover', 'mousedown', 'click', 'dblclick', 'onRowFocus']);
notesGrid.fireEvent("bind", notesGrid, { id: record.get('id') });
}
});
}
},
...
};
Ext.apply(me, Ext.apply(me.initialConfig, config));
me.callParent(arguments);
},
plugins: [{
ptype: 'rowexpander',
pluginId: 'abc',
rowBodyTpl: [
'<div id="CallsGridRow-{id}" ></div>'
]
}]
});