I have a problem finding the right solution for my web interface:
I have a table created with Datatables framework. On backend Flask
By default table is populated from Mysql DB with query SELECT * FROM test_table WHERE status != 'OK'
But I want to add a checkbox with the name Show all records.
By checking this checkbox backend should use another query to populate SELECT * FROM test_table
Basically, select everything.
Javascript code:
$('#main_table').DataTable({
"processing": true,
"ajax": {
"url": "/index_get_data",
"dataType": "json",
"dataSrc": "data",
"contentType": "application/json"
},
"columns": [{
"data": "id"
}, {
"data": "version"
}
]
});
Python in Flask
#app.route('/index')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/index_get_data')
def main_info():
if query_all is not None:
sql_query = "SELECT * FROM test_table "
else:
sql_query = "SELECT * FROM test_table WHERE status != 'OK'"
conn = mysql.connect()
cursor = conn.cursor()
cursor.execute(sql_query)
conn.close()
row_headers = [x[0] for x in cursor.description]
rv = cursor.fetchall()
json_data = []
for result in rv:
json_data.append(dict(zip(row_headers, result)))
return_data = {'data': json_data}
return json.dumps(return_data, indent=4, sort_keys=True, default=str)
Javascript function that catches checkbox checking event
const checkbox = document.getElementById('chkBox')
checkbox.addEventListener('change', (event) => {
if (event.target.checked) {
$.post("/select_all", {
select_all: "True"
});
}
})
And here, unfortunately, I stuck. I can't find correct way to reload page with non default query.
...
For the record, you could have achieved that feature entirely client-side, by loading your full data set upon table initialization, having hidden column 'status' and doing search('OK')/search('') against that column upon Show all records unchecked/checked.
However, if status = 'OK' records stand the least part of your dataset and entire list of records is required not that often, it would be faster to do that server-side, indeed.
For that, you simply need to trigger ajax.reload() against your table from within POST success callback:
const checkbox = document.getElementById('chkBox')
checkbox.addEventListener('change', (event) => {
if (event.target.checked) {
$.post("/select_all", {
select_all: "True",
success: () => $('#main_table').DataTable().ajax.reload()
});
}
})
I'm using dgrid 1.2.1 OnDemandGrid, and with it have tried both dstore 1.1.1 and 1.1.2 (Rest, SimpleQuery, Trackable). It seems no matter what I try I am unable to get the virtual scrolling to work.
My store is defined as:
seStore = new declare([Rest, SimpleQuery, Trackable])({
target: appUrl + "api/GET_ITEMS",
idProperty: "SID",
sortParam: "sort",
useRangeHeaders: true
});
Defined with the store is a method for the sorting and filtering:
seStore.getSECollection = function (sortFieldName, desc) {
var sFilter = {};
if (sArea != "") {
sFilter.AREA = sArea;
}
var coll = seStore.filter(sFilter).sort({ property: sortFieldName, descending: desc });
return coll;
}
Grid:
// Create a Grid instance
seGrid = new (declare([OnDemandGrid, Selection, DijitRegistry, Selector, Keyboard, Editor, ColumnHider, ColumnResizer, ColumnReorder]))({
id: "seGrid",
idProperty: "SID",
cellNavigation: true,
columns: seColumns,
collection: seStore.getSECollection("SID", true),
region: 'center',
selectionMode: "multiple",
keepScrollPosition: true,
query: { responseType: "json" },
getBeforePut: false,
farOffRemoval: Infinity, // larger than total height of data; never remove rows
minRowsPerPage: 25, // request more data at a time
maxRowsPerPage: 50,
pagingMethod: 'throttleDelayed',
queryRowsOverlap: 0,
//loadingMessage: "Loading data...",
noDataMessage: "No results found.",
showFooter: true
});
And backend REST service response provides the correct response where rItems is an array of items from my database query and rTotal is the total number of items in the database for this query:
HttpResponseMessage rm = new HttpResponseMessage(HttpStatusCode.OK);
string dgrJsonResults = Newtonsoft.Json.JsonConvert.SerializeObject(rItems, Formatting.None);
rm.Content = new StringContent(dgrJsonResults, System.Text.Encoding.UTF8);
rm.Content.Headers.ContentRange = new ContentRangeHeaderValue((long)start, (long)count, rTotal) { Unit = "items" };
The grid initally loads correctly with the first 25 items requested, but after this initial request once I scroll down to the bottom (item 25), a request to get the next range of data is not fired.
Can someone please help point me in the right direction?
For displaying my data in a table, I use the dataTable plugin. As I have a big amount of data, I use the serverside processing. My problem is, that I have some data fields in my obtained JSON, that I would like to change before i display it in the table.
Example: I get values, whether some equipment is available or not. This is written as "", 0 or 1 in datasbase, for displaying it I would like to convert these values to "Yes" or "No" or "N/A".
To initialise the table I use the following code:
table = $('#table').DataTable({
"ajax": "myurl",
"sPaginationType": "full_numbers",
"sAjaxDataProp":"",
"deferRender": true,
columns: [
{data: 'attributes.0.value'},
{data:'attributes.1.value'},
{data:'attributes.2.value'},
{data:'attributes.3.value'},
{data:'attributes.4.value'}],
});
To bind the conerting function directly in the data array doesn't work (like
{data: convert(attributes.0.value)},
There are some parameters for the datatable plugin, which I tried, but I'm not sure, if they can solve my problems. This is an example from the documentation of the plugin:
$('#example').dataTable( {
"ajax": {
"url": "data.json",
"data": function ( d ) {
d.extra_search = $('#extra').val();
}
}
});
Can I use the data parameter to solve my problems (when I tried this, d is always empty) or it there another possibility to change my values before I integrate them in a table?
You could preprocess the JSON in an ajax.dataSrc function, but since you really just need to modify how the values are shown I would go for column rendering :
columns: [
{ data: 'attributes.0.value',
render: function(data, type, row) {
switch(data) {
case '0' : return 'No'; break;
case '1' : return 'Yes'; break;
default : return 'N/A'; break;
}
},
{ data: 'attributes.1.value' },
{ data: 'attributes.2.value' },
{ data: 'attributes.3.value' },
{ data: 'attributes.4.value' }
]
Use the above render method to each of the column values you need to convert.
I'm developing an application which is build in ExtJS 4.2.2 with symfony2 backend. Now I have following problem :
Lets say that I have 2 mvc's in my frontend - One for managing users and other for managing their data.
Now when user want to delete row of data I have to set his name in that deleted record so it can be archived and other users would know who made the deletion.
The question is how can I access to currently logged in user data from my DataController.
I've tried this:
This is part of code in my DataController, it's responsible for archiving deleted record
//sel[0] is selected record
//console.log({{ app.user.username }}); //I tought it could work somehow :) but it did not
sel[0].set('deleted_at', new Date()); //setting date of deletion
//get user store
//var usrStore = Ext.getStore('common_user.user_store');
var usrStore = Ext.data.StoreManager.lookup('common_user.user_store')
console.log(usrStore); //in both cases ruterns undefined
//sel[0].set('deleted_by', ); //here i want to save user name in column named "deleted_by"
sel[0].save();
sel[0].commit();
grid.getStore().sync();
grid.getStore().reload(); //reload grid
grid.getStore().remove(sel[0]); //remove from grid
This is how I've configured User store proxy
proxy: {
type: 'rest',
url: Routing.generate('webit_sencha_get',{store: 'common_user.user_store'}),
appendId: false,
batchActions: true,
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json',
root: 'data',
encode: true,
writeAllFields: true
}
}
Maybe I should load User grid on init of my data controller ? But still, I don't know how to.
Ext.define('Data.controller', {
extend: Ext.app.Controller,
init: function() {
this.control({
...
'data_grid': {
afterrender: this.onDataGridRender
},
'archive_grid': {
afterrender: this.onDataArchiveGridRender
},
'common_user_grid': {
afterrender: this.onUserGridRender // ????
}
});
},
...
So the question is how can I access (if it's possible) name of currently logged in user from other controller
I'll be thankfull for any guidance.
Problem fixed, I just had to pass parameter to my backend save option and then get current user there
I'm currently trying to make changes to an existing DB using the migrations plugin for PersistenceJS. I can add/edit/delete items in the DB just fine — but…
How to add a column to an existing(!) table?
How to change the type of an existing(!) column, e.g. from 'text' to 'integer'?
These changes should retain currently existing data.
Sadly, the documentation is a little scarce, maybe you could help?
Here's the current, working setup:
persistence.store.websql.config(persistence, 'tododatabase', 'todos are fun', 5*1024*1024);
var Todo = persistence.define('Todo', {
task: 'TEXT',
priority: 'INT',
done: 'BOOL'
});
persistence.schemaSync();
function addTodo( item ){
var todo = new Todo();
todo.task = item.task;
todo.priority = item.priority;
todo.done = item.done;
persistence.add(todo);
persistence.flush();
};
function deleteTodo( item, callback ){
// item.id was created automatically by calling "new Todo()"
Todo.all().filter('id','=', item.id ).destroyAll( function(){
persistence.flush( callback );
});
};
The migration code that kinda works:
persistence.defineMigration(1, {
up: function() {
this.createTable('Todo', function(t){
t.text('task');
t.integer('priority');
t.boolean('done');
});
},
down: function() {
this.dropTable('Todo');
}
});
persistence.defineMigration(2, {
up: function() {
this.addColumn('Todo', 'due', 'DATE');
},
down: function() {
this.removeColumn('Todo', 'due');
}
});
function migrate( callback ){
console.log('migrating...');
persistence.migrations.init( function(){
console.log('migration init');
// this should migrate up to the latest version, in our case: 2
persistence.migrate( function(){
console.log('migration complete!');
} );
});
}
Results…
calling migrate() will only log up to "migration init", the complete handler is never called, the "due" column is not created
not calling schemaSync() before calling migrate() as Zef Hemel himself proposed in this post yields the same result as 1.
changing the first line to persistence.store.websql.config(persistence, 'newdatabase', 'testing migration', 5*1024*1024);, not calling schemaSync() and only calling migrate() will successfully log "migration complete!" — but it does so in a new, completely empty database "newdatabase", which will of course not retain any exsiting data.
Summary
There is a database that was created using persistence.store.websql.config(...), persistence.define('Todo',...) and persistence.schemaSync().
I now want to keep all the data that already exist in that database, but want to
change the type of column priority from 'integer' to 'text'
add a column due with type 'date' to all existing Todos
If you could push me in the right direction, I'd greatly appreciate it!
Thanks!
I finally got it working. There are a number of issues with my initial requirements that I'd like to point out for future reference. Take a look at the first migration definition:
persistence.defineMigration(1, {
up: function() {
this.createTable('Todo', function(t){
...
Not surprisingly, createTable will do exactly that: it will execute the SQL statement 'CREATE TABLE Todo ...', which will silently fail and halt the migration if there is a table with the name Todo already. This is why it worked with a new database, but not with the existing one. Bear in mind: I already had a live database with a table "Todo" that needed updating. If you're starting fresh (i.e. you've not used schemaSync), createTable works just fine. Since the Migrations plugin does not provide a createTableIfNotExists method, I needed to utilize executeSql as follows:
persistence.defineMigration(1, {
up: function() {
this.executeSql('CREATE TABLE IF NOT EXISTS Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, done BOOL)');
...
Now that the migration from schema version 0 to 1 succeeded, the migration to version 2 was successful as well.
With the migration to version 3 the type of the priority column needed to change from int to text. This would normally be done using the ALTER COLUMN SQL command, wich is not supported by Web SQL / SQLite. See Omitted Features for SQLite.
Altering a column with SQLite requires a 4-step workaround:
persistence.defineMigration(3, {
up: function() {
// rename current table
this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
// create new table with required columns and column types
this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority TEXT, done BOOL)');
// copy contents from old table to new table
this.executeSql('INSERT INTO Todo(id, task, priority, done) SELECT id, task, priority, done FROM OldTodo');
// delete old table
this.executeSql('DROP TABLE OldTodo');
},
...
Of course, after changing the column type, the entity definition for 'Todo' should also be changed:
var Todo = persistence.define('Todo', {
task: 'TEXT',
priority: 'TEXT', // was 'INT'
due: 'DATE',
done: 'BOOL'
});
And finally, the complete source:
persistence.store.websql.config(persistence, 'tododatabase', 'todos are fun', 5*1024*1024);
// persistence.debug = true;
//v0 + v1
// var Todo = persistence.define('Todo', {
// task: 'TEXT',
// priority: 'INT',
// done: 'BOOL'
// });
//v2
// var Todo = persistence.define('Todo', {
// task: 'TEXT',
// priority: 'INT',
// due: 'DATE',
// done: 'BOOL'
// });
//v3
var Todo = persistence.define('Todo', {
task: 'TEXT',
priority: 'TEXT',
due: 'DATE',
done: 'BOOL'
});
persistence.defineMigration(1, {
up: function() {
this.executeSql('CREATE TABLE IF NOT EXISTS Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, done BOOL)');
},
down: function() {
this.dropTable('Todo');
}
});
persistence.defineMigration(2, {
up: function() {
this.addColumn('Todo', 'due', 'DATE');
},
down: function() {
this.removeColumn('Todo', 'due');
}
});
persistence.defineMigration(3, {
up: function() {
// rename current table
this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
// create new table with required columns
this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority TEXT, due DATE, done BOOL)');
// copy contents from old table to new table
this.executeSql('INSERT INTO Todo(id, task, priority, due, done) SELECT id, task, priority, due, done FROM OldTodo');
// delete current table
this.executeSql('DROP TABLE OldTodo');
},
down: function() {
this.executeSql('ALTER TABLE Todo RENAME TO OldTodo');
this.executeSql('CREATE TABLE Todo (id VARCHAR(32) PRIMARY KEY, task TEXT, priority INT, due DATE, done BOOL)');
this.executeSql('INSERT INTO Todo(id, task, priority, due, done) SELECT id, task, priority, due, done FROM OldTodo');
this.executeSql('DROP TABLE OldTodo');
}
});
function migrate( callback ){
console.log('migrating...');
persistence.migrations.init( function(){
console.log('migration init');
persistence.migrate( function(){
console.debug('migration complete!');
callback();
} );
});
};
migrate( onMigrationComplete );
function onMigrationComplete(){
// database is ready. do amazing things...
};
That's a great explanation, thank you! But I think I know an easier way to achieve this.
I got in the same trouble like you: I'v got a set of schemas described with persistence.define and created with persistence.schemaSync.
So this is my particular case:
// This is my mixin for all schemas
var Versioned = persistence.defineMixin('Versioned', {
serverId: "TEXT",
intVersion: "INT",
dtSynced: "DATE",
dtCreatedAt: "DATE",
dtUpdatedAt: "DATE",
delete: "BOOL",
update: "BOOL",
add: "BOOL",
isReadOnly: "BOOL"
});
// This is one of the schemas I need to update with a new field.
var Person = persistence.define('Person', {
fullName: "TEXT",
rate: "INT"
});
//... More schema definitions
// Setup mixin
Person.is(Versioned);
// Sync schemas
persistence.schemaSync();
Ok. Nothing special about it. Now after a few months my app's being in production I want to add a new field isEmployed to the Person schema.
According to the docs I should rewrite all of my schema definitions to the migrations and to stop using persistence.schemaSync(). But I don't want to rewrite all of my definitions. Instead of it I define a new migration right behind the PersistenceJS init code:
// Init ORM
persistence.store.websql.config(
persistence,
'Sarafan',
'0.0.2',
'Sarafan.app database',
100 * 1024 * 1024,
0
);
// Define Migrations
persistence.defineMigration(1, {
up: function () {
this.addColumn('Person', 'isEmployed', 'BOOL');
}
});
// ... describing isVersioned mixin
// Updated schema definition with a new field 'isEmployed'
var Person = persistence.define('Person', {
fullName: "TEXT",
rate: "INT",
isEmployed: "BOOL"
});
//... More schema definitions
// Setup mixin
Person.is(Versioned);
// Apply the migration right away from the schemaSync call.
persistence.schemaSync(function (tx) {
persistence.migrations.init(function () {
persistence.migrate(function(){
// Optional callback to be executed after initialization
});
});
});
So that's it! I tested this approach only to add new fields to the schema.
Let me know if it does or doesn't work for you.