I want to implement dynamic grid in Extjs. I have found this How do you create table columns and fields from json? (Dynamic Grid) and the accepted answer looks good.
In my case I have no proxy store but a proxy model:
fields: [ {name: 'id', type: 'int', persist: false},
{name: 'gender', type: 'auto'},
{name: 'name', type: 'auto'},
{name: 'age', type: 'int'}],
identifier: 'sequential', // to generate -1, -2 etc on the client
proxy: {
type: 'rest',
idParam: "id",
url:'http://localhost:3000/posts',
api:
{
read : 'http://localhost:3000/db',
create: 'http://localhost:3000/posts',
update : 'http://localhost:3000/posts' ,
destroy : 'http://localhost:3000/posts'
},
headers: {'Content-Type': "application/json" },
reader: {
type: 'json',
rootProperty:'posts',
totalProperty: 'total'
},
writer: {
type: 'json'
}
My store looks like is:
model: 'ThemeApp.model.peopleModel',
storeId: 'peopleStore',
pageSize: 500,
autoLoad: true,
autoSync: true,
pageSize: 5,
autoLoad: {start: 0, limit: 5},
autoSync: true,
sorters: [{
property : 'age',
direction:'ASC'
}],
groupField: 'gender'
});
In my view I have defined columns:[] But I don't know where to call metachange function.
Can anyone tell me where to use metachange function and should I use metachange function of store or proxy?
Generally you don't want to configure proxy on the model, that is only useful when you want to use standalone Model instances without a store.
Move the proxy config to the Store, and make the server respond to read requests with additional metadata object, which you can then use in the metachange event handler to configure the grid.
Using Reader metadata is the right way to do "dynamic" Grids.
I have tried to create a new empty record without creating a model.
Accidentally I tried to run this:
new new storeComp.model()
and it actually works!
anyone know any solution for this or an answer to how is it working ?
Thanks!
Shalev
Stores that are not configured with a model will create an anonymous one implicitly. This is explained in the docs for the implicitModel private property (the store needs to know that because if it uses an implicit model, it must destroy it when it is itself destroyed).
In the end that means that you can safely expect any store to be backed with a model.
I faced the same problem for an associative store. For that i have used below JSON format to load project store. So automatically empty record is created in associative (project-resources) Store.
Store:
Ext.define('App.store.Projects', {
extend : 'Ext.data.Store',
model : 'App.model.Project'
});
Models:
Ext.define("App.model.Project", {
extend: 'Ext.data.Model',
idProperty : 'id',
uses : ['App.model.ProjectResource'],
fields: [
{ type: 'auto', name: 'id'},
{ type: 'number', name: 'customerid'},
{ type: 'string', name: 'projectcode'}
],
associations: [
{type: 'hasMany', model: 'App.model.ProjectResource', name: 'Resources', storeConfig: { remoteFilter: true }}
]
});
Ext.define("App.model.ProjectResource", {
extend: 'Ext.data.Model',
idProperty : 'id',
fields: [
{ type: 'number', name: 'id'},
{ type: 'string', name: 'name'},
{ type: 'number', name: 'projectid'},
{ type: 'auto', name: 'resourceid'}
],
associations : [
{type : 'belongsTo', model: 'App.model.Project'}
]
});
JSON Format:
[
{
"id": "105",
"customerid": "105",
"projectcode": "ALN01",
"Resources": {}
}
]
Loading store with empty object (like "Resources": {}) will create empty model.
I have a small problem if you can help me it would be great. I cant insert my own ID to the local storage.
ext.define('EMC.model.ReferenceData', {
extend: 'Ext.data.Model',
config: {
identifier: {
type: 'simple'
},
idProperty: 'Id',
fields: [
{name: 'Id', type: 'string'},
{name: 'Description', type: 'string'},
{name: 'syncChangeVersion', type: 'integer', mapping: 'SysChangeVersion'},
{name: 'TypeEnum', type: 'string'}
]
}
});
this is a model of my project and the store is shown below.
Ext.define("EMC.store.SyncReferenceData", {
extend : "Ext.data.Store",
requires : [ 'Ext.data.proxy.LocalStorage' ],
config : {
model : 'EMC.model.ReferenceData',
storeId : 'referenceStore',
sorters : 'Id',
proxy : {
type : 'localstorage',
id : 'reference_data'
},
autoSync : true,
autoLoad : true
}
});
this is the phrase I created to insert data to the local storage using controller.
var frmModel2 = Ext.create('EMC.model.ReferenceData',{
Id : '13',
Description : 'lole',
syncChangeVersion : 0,
TypeEnum : '199'
});
ReferenceStore.add(frmModel2);
ReferenceStore.sync();
But when I see the local storage it is empty. But If i comment Id : '13' then it will add to the local storage with a id of "ext-record-293".
why cant I inset my own Id to the store?
Try like this..
Ext.define('EMC.model.ReferenceData', {
extend: 'Ext.data.Model',
config: {
dProperty : 'uniqueid', // dummy name(not a field)
clientIdProperty : 'Id',
fields: [
{name: 'Id', type: 'string'},
{name: 'Description', type: 'string'},
{name: 'syncChangeVersion', type: 'integer', mapping: 'SysChangeVersion'},
{name: 'TypeEnum', type: 'string'}
]
}
});
I am trying to load ext data stores using jsonp. The below code is working fine when i am using ajax and making requests on the same domain.
The store definition:
var baseUrl = 'http://localhost:8090/';
Ext.define('Ktimatologio.store.NewWholeBlockStore', {
extend: 'Ext.data.Store',
alias: 'widget.newsingleblockstore',
requires: ['Ktimatologio.model.NewWholeBlockModel'],
model: 'Ktimatologio.model.NewWholeBlockModel',
groupField: 'search_tag',
fields: [
{name:'id', mapping:'id'},
{name:'id1', mapping:'id1'},
{name: 'text', mapping: 'text'},
{name: 'title', mapping: 'title'},
{name: 'fek', mapping: 'fek'},
{name: 'date', mapping: 'date'},
{name: 'descr', mapping: 'description'},
{name: 'model', mapping: 'model'},
{name: 'body', mapping: 'body'},
{name: 'type', mapping: 'type'},
{name: 'history', mapping: 'history'},
{name: 'src', mapping: 'url'},
{name: 'search_tag', mapping: 'search_tag'},
{name: 'new_element', mapping: 'new_element'},
{name: 'new_table', mapping: 'new_table'}
],
autoLoad: true,
proxy: {
//type:'ajax',
type:'jsonp',
url: baseUrl + 'openbd/ktimatologio-final/resources/cfScripts/nea_stoixeia/GetNewTables.cfc?',
extraParams: {
method: 'getNewTables'
},
reader:{
type: 'json',
root: 'data'
}
}
When i run the code i see an error on firebug:
SyntaxError: invalid label
{"data":[{"id":"1_n_2308_1995","id1":1,"title":"Άρθρο 1 ΦΕΚ Α΄ 114 15.6....
GetNew...llback2 (line 1, column 1)
I am stuck!
Any help is much appreciated!
Tom
Greece
JSONP service must actually return a function call not just JSON back. Google for some examples.
I've asked a similar question before, here: https://stackoverflow.com/questions/11707007/nested-json-form-submits-in-extjs4-getting-the-writer-to-remap-the-fields
Also asked in the Sencha forums, am pretty desperate:
I have many problems with reading and writing nested json files into an associated model.
I will write all the code that I can here, in the hope that some of you will find what I'm doing wrong.
Questions are emphasized in the text.
The json I am receiving looks like this, and can not be changed: tests:
[
{
"name":"qwerty",
"id":"1",
"uid":"1",
"created":"1341481071",
"changed":"1343804076",
"status":"1",
"jmeterOptions":{
"jmx":"\/files\/20141\/multi.jmx",
"engines":"0",
"version":"2.5.1",
"consoleArgs":" -t sample.jmx -JDelay=10000 -JEmbedded=1 -JRampup=1800 -JUsers=10",
"engineArgs":" -JDelay=10000 -JEmbedded=1 -JRampup=1800 -JUsers=10",
"instanceType":"m1.medium",
"overrideOptions":{
"threads":"50",
"rampUpTime":"300",
"iterations":"-1",
"duration":"-1"
}
},
"testOptions":{
"type":"BM_TEST_TYPE_AUTO",
"geo":"us-east-1",
"timeout":"1",
"reportByEmail":"1",
"launchTime":"0",
"sessionId":"",
"hosts":null,
"privateIps":null
},
"autoOptions":{
"users":"10",
"delay":"10",
"rampUpTime":"1800",
"pages":[
{
"label":"sencha",
"url":"http:\/\/sencha.com"
}
]
},
"seleniumOptions":{
"pages":[
{
"label":"sencha",
"url":"http:\/\/sencha.com"
}
]
}
},
{
"name":"Instant Load test",
"id":"2",
"uid":"1",
"created":"1336921297",
"changed":"1341132949",
"status":"1",
"jmeterOptions":{
"jmx":null,
"engines":"0",
"version":"2.5.1",
"consoleArgs":" -t sample.jmx -JDelay=10000 -JEmbedded=1 -JRampup=1800 -JUsers=10",
"engineArgs":" -JDelay=10000 -JEmbedded=1 -JRampup=1800 -JUsers=10",
"instanceType":"m1.medium",
"overrideOptions":null
},
"testOptions":{
"type":"BM_TEST_TYPE_AUTO",
"geo":null,
"timeout":"1",
"reportByEmail":"0",
"launchTime":"0",
"sessionId":"",
"hosts":null,
"privateIps":null
},
"autoOptions":{
"users":"10",
"delay":"10",
"rampUpTime":"1800",
"pages":[
{
"label":"",
"url":"http:\/\/cnn.com"
}
]
},
"seleniumOptions":{
"pages":[
{
"label":"",
"url":"http:\/\/cnn.com"
}
]
}
}
]
The model looks like this:
Ext.define('BM.model.Test', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'id', type: 'id'},
{name: 'uid', type: 'mumber'},
{name: 'created', type: 'date', dateFormat: 'timestamp'},
{name: 'changed', type: 'date', dateFormat: 'timestamp'},
{name: 'status', type: 'number'},
{name: 'jmeterJmx', mapping: 'jmeterOptions.jmx', type: 'string'},
{name: 'jmeterEngines', mapping: 'jmeterOptions.engines', type: 'number'},
{name: 'jmeterVersion', mapping: 'jmeterOptions.version', type: 'string'},
{name: 'jmeterConsoleArgs', mapping: 'jmeterOptions.consoleArgs', type: 'string'},
{name: 'jmeterEngineArgs', mapping: 'jmeterOptions.engineArgs', type: 'string'},
{name: 'jmeterInstanceType', mapping: 'jmeterOptions.instanceType', type: 'string'},
{name: 'testOptionsType', mapping: 'testOptions.type', type: 'string'},
{name: 'testOptionsGeo', mapping: 'testOptions.geo', type: 'string'},
{name: 'testOptionsTimeout', mapping: 'testOptions.timeout', type: 'number'},
{name: 'testOptionsReportByEmail', mapping: 'testOptions.reportByEmail', type: 'string'},
{name: 'testOptionsLaunchTime', mapping: 'testOptions.launchTime', type: 'string'},
{name: 'testOptionsSessionId', mapping: 'testOptions.sessionId', type: 'string'},
{name: 'testOptionsHosts', mapping: 'testOptions.hosts', type: 'string'},
{name: 'testOptionsPrivateIps', mapping: 'testOptions.privateIps', type: 'string'},
{name: 'autoUsers', mapping: 'autoOptions.users', type: 'number'},
{name: 'autoDelay', mapping: 'autoOptions.delay', type: 'number'},
{name: 'autoRampUpTime', mapping: 'autoOptions.rampUpTime', type: 'number'}
],
hasMany: [
{model: 'Pages', name: 'autoPages', associationKey: 'autoOptions.pages'},
{model: 'Pages', name: 'seleniumPages', associationKey: 'seleniumOptions.pages'}
],
proxy: {
type: 'ajax',
api: {
read: '../webapp/get/tests',
update: '../webapp/set/test'
},
reader: {
type: 'json',
root: 'tests',
successProperty: 'success'
},
writer: new Ext.data.JsonWriter({
encode: false,
writeAllFields: true,
getRecordData: function (record) {
Ext.apply(record.data,record.getAssociatedData(true));
return record.getAssociatedData(true);
}
})
}
});
Ext.define('Pages', {
extend: 'Ext.data.Model',
fields: [
{name: 'label', type: 'string'},
{name: 'url', type: 'string'}
]
});
First Question: Including the following four lines causes the grid to load empty, why?
{name: 'jmeterOverrideThreads', mapping: 'jmeterOptions.overrideOptions.threads', type: 'number'},
{name: 'jmeterOverrideRampUpTime', mapping: 'jmeterOptions.overrideOptions.rampUpTime', type: 'number'},
{name: 'jmeterOverrideIterations', mapping: 'jmeterOptions.overrideOptions.iterations', type: 'number'},
{name: 'jmeterOverrideDuration', mapping: 'jmeterOptions.overrideOptions.duration', type: 'number'},
Second Question: The associated data comes up empty. rawData has the json as shown above, how do I make getAssociatedData to fill in the correct data?
Better yet, Why aren't my associations working?
Third Question: The writer now only sends the empty associated data, how do I send all data, exactly as I received it?
My solution to this is dead ugly:
updateTest: function(button) {
var form = button.up('panel');
var record = form.getRecord(),
values = form.getValues();
record.set(values);
var rd = record.data;
// this.getTestsStore().sync(); //No! Sends flattened data.
// var autoPages = record.getAssociatedData().autoPages; // Is currently empty
// var seleniumPages = record.getAssociatedData().seleniumPages; // Is currently empty
var autoPages = [];
var seleniumPages = [];
var iterator = 0;
var autoPagesLabel = record.data['autoPagesLabel' + iterator];
var autoPagesUrl = record.data['autoPagesUrl' + iterator];
while (autoPagesLabel != undefined){
autoPages.push({
'label': autoPagesLabel,
'url': autoPagesUrl
});
iterator++;
autoPagesLabel = record.data['autoPagesLabel' + iterator];
autoPagesUrl = record.data['autoPagesUrl' + iterator];
}
iterator = 0;
var seleniumPagesLabel = record.data['seleniumPagesLabel' + iterator];
var seleniumPagesUrl = record.data['seleniumPagesUrl' + iterator];
while (seleniumPagesLabel != undefined){
seleniumPages.push({
'label': seleniumPagesLabel,
'url': seleniumPagesUrl
});
iterator++;
seleniumPagesLabel = record.data['seleniumPagesLabel' + iterator];
seleniumPagesUrl = record.data['seleniumPagesUrl' + iterator];
}
var reformattedJson = {
"name": rd.name,
"id": rd.id,
"uid": rd.uid,
"created": rd.created,
"changed": rd.changed,
"status": rd.status,
"jmeterOptions":{
"jmx": rd.jmeterJmx,
"engines": rd.jmeterEngines,
"version": rd.jmeterVersion,
"consoleArgs": rd.jmeterConsoleArgs,
"engineArgs": rd.jmeterEngineArgs,
"instanceType": rd.jmeterInstanceType,
"overrideOptions": {
"threads": rd.jmeterOverrideThreads,
"rampUpTime": rd.jmeterOverrideRampUpTime,
"iterations": rd.jmeterOverrideIterations,
"duration": rd.jmeterOverrideDuration
}
},
"testOptions":{
"type": rd.testOptionsType,
"geo": rd.testOptionsGeo,
"timeout": rd.testOptionsTimeout,
"reportByEmail": rd.testOptionsReportByEmail,
"launchTime": rd.testOptionsLaunchTime,
"sessionId": rd.testOptionsSessionId,
"hosts": rd.testOptionsHosts,
"privateIps": rd.testOptionsPrivateIps
},
"autoOptions":{
"users": rd.autoUsers,
"delay": rd.autoDelay,
"rampUpTime": rd.autoRampUpTime,
"pages": autoPages
},
"seleniumOptions":{
"pages":seleniumPages
}
};
Ext.Ajax.request({
url: '../webapp/set/test',
method:'Post',
jsonData: reformattedJson,
success: function(response){
var text = response.responseText;
// process server response here
console.log('Post successfull! ');
}
});
}
This is the form:
Ext.define('BM.view.test.Edit', {
extend: 'Ext.form.Panel',
alias: 'widget.test-edit',
layout: 'anchor',
title: 'Edit Test',
defaultType: 'textfield',
items: [
{name: 'id', hidden: true},
{name: 'name', fieldLabel: 'Name'},
{name: 'status', fieldLabel: 'Status'},
{name: 'testOptionsType', fieldLabel: 'Type'},
{name: 'autoUsers', fieldLabel: 'User count'}
],
buttons: [
{
text: 'Save',
action: 'save'
},
{
text: 'Cancel',
scope: this,
handler: this.close
}
]
});
This is what calls the form: (Clicking on a grid row)
editTest: function(grid, record) {
var view = Ext.widget('test-edit');
var viewPort = Ext.ComponentQuery.query('viewport')[0];
var autoPages = record.raw.autoOptions.pages;
var seleniumPages = record.raw.seleniumOptions.pages;
for (var i =0; i < autoPages.length; i++){
var tf = Ext.create('Ext.form.field.Text', {
id: 'autoPagesLabel' + i,
name: 'autoPagesLabel' + i,
fieldLabel: 'Label',
value: autoPages[i].label,
columnWidth:0.5
});
view.add(tf);
tf = Ext.create('Ext.form.field.Text', {
id: 'autoPagesUrl' + i,
name: 'autoPagesUrl' + i,
fieldLabel: 'Url',
value: autoPages[i].url,
columnWidth:0.5
});
view.add(tf);
}
view.loadRecord(record);
viewPort.layout.centerRegion.removeAll();
viewPort.layout.centerRegion.add(view);
}
I tried following the MVC architecture of Ext JS 4, note by note from the tutorials, and failed miserably.
What am I doing wrong?
Do you need any other information or pieces of code? Just tell me and I'll post them.
I found a solution that works for some of my questions.
As a bypass to my first question, I find that mapping of more than two levels is problematic, so I added a hasOne relationship. I don't think it's a good solution, but have no others, would love a comment about this.
{name: 'jmeterOverrideThreads', mapping: 'jmeterOptions.overrideOptions.threads', type: 'number'},
{name: 'jmeterOverrideRampUpTime', mapping: 'jmeterOptions.overrideOptions.rampUpTime', type: 'number'},
{name: 'jmeterOverrideIterations', mapping: 'jmeterOptions.overrideOptions.iterations', type: 'number'},
{name: 'jmeterOverrideDuration', mapping: 'jmeterOptions.overrideOptions.duration', type: 'number'},
from the model, becomes:
hasOne: [
{model: 'OverrideOptions', associationKey: 'jmeterOptions', reader: {root: 'overrideOptions'}}
],
// and:
Ext.define('OverrideOptions', {
extend: 'Ext.data.Model',
fields: [
{name: 'jmeterOverrideThreads', mapping: 'threads', type: 'number'},
{name: 'jmeterOverrideRampUpTime', mapping: 'rampUpTime', type: 'number'},
{name: 'jmeterOverrideIterations', mapping: 'iterations', type: 'number'},
{name: 'jmeterOverrideDuration', mapping: 'duration', type: 'number'}
]
});
As for my second question about the associations not working:
In my model, instead of:
hasMany: [
{model: 'Pages', name: 'autoPages', associationKey: 'autoOptions.pages'},
{model: 'Pages', name: 'seleniumPages', associationKey: 'seleniumOptions.pages'}
],
I added:
hasMany: [
{model: 'Pages', name: 'autoPages', associationKey: 'autoOptions', reader: {root: 'pages'}},
{model: 'Pages', name: 'seleniumPages', associationKey: 'seleniumOptions', reader: {root: 'pages'}}
],
Suddenly, I am able to access my data from record.getAssociatedData(), it is still flattened, but at least I have the data.
I still don't have a good idea about having the writer output the data in its original format other than writing it as I did in brute force.
Would again love some feedback on that.
Other questions arose:
I want my associated data to be added to a form. As we are talking about arrays of objects, we don't know how many there will be, is there a better way to create the form from associated data than the one I used in my original question. Is there a better way to access the form data, other than naming the fields with a running iterator: a1, a2,...
Thanks in advance for any answer.