ExtJS 4.1 - Nested hasOne associated in JSON - extjs

I have JSON in the following format:
{
"id": 1,
"arbitraryAttribute": "arbitraryValue",
"thing": {
"thingKey1": "thingValue1",
"thinkKey2": "thingValue2"
}
}
This model is represented like so:
Ext.define("MyModel", {
extends: "Ext.data.Model",
fields: [
{name: "id", type: "string"},
{name: "arbitraryAttribute", type: "string"},
],
hasOne: [
{
model: "Thing",
name: "thing"
}
]
});
Ext.define("Thing", {
extends: "Ext.data.Model",
fields: [
{name: "thingKey1", type: "string"},
{name: "thingKey2", type: "string"}
]
});
The proxy is a simple json proxy. The JSON it is getting looks like that which I presented, but my record seems to have no knowledge of the Thing model. Is there any additional plumbing I need to set up to get MyModel to pull the nested Thing json?

Your forgot to set the thing_id in MyModel. Also this doesnt work at all in ExtJs.
You can now set the thing_id via JSON but not the whole object as you do (and it should).
It will autoLoad the complete object via the models proxy if required.

Ext.define('User', {
extend:'Ext.data.Model',
fields: ['id', 'name', 'status'],
associations: [
{ type: 'hasOne', model: 'Status', associationKey: 'status' }
]
});
Ext.define('Status', {
extend:'Ext.data.Model',
fields: ['id', 'title'],
});
Demo here

Related

Define mapping in reader

I have defined a custom ComboBox component, that I want to re-use with multiple Stores. It has a simple config as below.
Ext.define('Myapp.CustomCombo', {
extend: 'Ext.form.ComboBox',
valueField: 'id',
displayField: 'name'
});
The model is
Ext.define('Myapp.model.ComboModel',{
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' },
{ name: 'linkCls', type: 'string' }
]
});
I define multiple stores each with its own proxy config & identified by a unique storeId, as below
Ext.create('Myapp.store.EmployeeComboStore', {
model: 'Myapp.model.ComboModel',
storeId: 'employeeLOVComboStore',
proxy: {
type: 'rest',
url: '/employees/getLovData',
reader: {
type: 'json',
rootProperty: 'data'
}
}
});
The server responds with a json as below
{
"data" : [
{"employeeId": 1, "employeeName": "Chris"},
{"employeeId": 2, "employeeName": "Jack"},
]
}
I can have multiple such stores, like departmentStore with a different proxy URL and where in, the server response could be
{
"data" : [
{"departmentId": 1, "departmentName": "Sales"},
{"departmentId": 2, "departmentName": "Marketing"},
]
}
I want to define a mapping in the Reader, with instructions to map data differently in different stores, as in.. employeeId should be mapped to 'id' & employeeName to 'name' in employeeStore, whereas departmentId should be mapped to 'id' & departmentName to 'name' in departmentStore.
I've seen that there are options to define mapping for each field in a Model, but I would want to define a mapping in a Reader, since the server responds with data from a relational database where field names would be the columnNames.
What you can do, is to send along metaData from the server to the client.
The json response can look like this:
{
data: [{ ... }],
msg: "...",
total: 99,
metaData: {
fields: [{ ... }],
columns: [{ ... }],
idProperty: "id",
messageProperty: "msg",
root: "data"
}
}
In you case it would be sufficient if the json response would look like this
{
data: [{ ... }],
metaData: {
fields: [
{ name: 'Id', type: 'int', mapping: 'departmentId' },
{ name: 'Name', type: 'string', mapping: 'departmentName' },
]
}
}
Here is also a good example how it works: Basic Meta Data Config

ExtJS load data to Form from Store with association

I have a single form that has fields for a Employee Model and fields for Person Model.
Employee Model
Ext.define('App.model.Employee', {
extend: 'Ext.data.Model',
fields: [{
name: 'EmployeeId',
type: 'int'
},
{
name: 'PersonId',
type: 'int'
},
'EmployeeNumber',
'JoinDate'
],
belongsTo: 'App.model.Person'
});
Person Model
Ext.define('App.mode.Person', {
extend: 'Ext.data.Model',
fields: [{
name: 'PersonId',
type: 'int'
},
'FirstName',
'LastName'
],
hasMany: {
model: 'App.model.Employee',
name: 'Employees'
}
});
My Store:
var store = Ext.create('Ext.data.Store', {
model: 'App.model.Employee',
autoLoad: false,
autoSync: true,
proxy: {
type: 'ajax',
api: {
read: 'api/employee/getemployee'
},
reader: {
type: 'json',
successProperty: 'success',
root: 'data',
messageProperty: 'message'
}
}
});
This is my actual JSON
"data": [{
"EmployeeId": 5,
"PersonId": 1,
"EmployeeNumber": 2001,
"JoinDate" : "",
"Person": {
"PersonId": 1,
"FathersLast": "SMITH",
"FirstName": "JOHN"
}
}],
My question is how does my json response (from server) should be structured in order to load store successful?
You can find explanation and sample code in Ext data package guide section "Loading Nested Data". Other example is in the heading of Association documentation. Both with sample JSON files showing the required structure.
After looking at your code and json structure i can recognize that each employee has a person object, so you need to correct your mapping. Instead of Defining hasMany relationship in person model, you should define hasOne relationship for person in employee.
hasOne: {
model: 'App.mode.Person',
name: 'Person'
}
Please try this out. Your json structure looks good.

Associations and class names

I tried to build a very simple application that creates a store based on two associated models. (It is based on an example you can find here.) This works if I use a shore class name. However, it does not work anymore as soon as I use the class name with the path and the application name, as I was used to do.
I made the following changes:
Changed "Pessoa" to "Aap.model.Pessoa"
Changed "Endereco" to "Aap.model.Endereco"
Could somebody please explain to me:
What is the underlying problem?
What do I have to change in order to make the assosiations work in the second example
Example that works
app.js:
Ext.Loader.setConfig({
enabled: true, // Allows dynamc loading of JavaSCript files
disableCaching: false // Disable random parameter in the URLs path});
Ext.application({
name: 'Aap',
models: ['Pessoa', 'Endereco']});
app/model/Pessoa.js:
Ext.define('Pessoa', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'nome', type: 'string'}
],
proxy: {
type: 'rest',
url: 'data/data',
format: 'json'
},
hasOne: {model: 'Endereco', foreignKey: 'pessoaId'} }
app/model/Endereco.js:
Ext.define('Endereco', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'logradouro', type: 'string'}
]});
data/data.json:
[
{
"id": 1,
"nome": "Loiane",
"sobrenome": "Groner",
"endereco": {
"id": 14,
"logradouro": "rua ficticia",
"numero": "100"
}
},
{
"id": 2,
"nome": "Tom",
"sobrenome": "Stock",
"endereco": {
"id": 34,
"logradouro": "reality street",
"numero": "55"
}
}]
Command in the browser console:
pessoaStore = new Ext.data.Store({
autoLoad: true,
model: 'Pessoa'
});
As a result I get a store with two properly associated model. If I change the class name, however, association does not work anymore.
Example that does not work
app.js:
Ext.Loader.setConfig({
enabled: true, // Allows dynamc loading of JavaSCript files
disableCaching: false // Disable random parameter in the URLs path});
Ext.application({
name: 'Aap',
models: ['Aap.model.Pessoa', 'Aap.model.Endereco']});
app/model/Pessoa.js:
Ext.define('Aap.model.Pessoa', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'nome', type: 'string'}
],
proxy: {
type: 'rest',
url: 'data/data',
format: 'json'
},
hasOne: {model: 'Aap.model.Endereco', foreignKey: 'pessoaId'} }
app/model/Endereco.js:
Ext.define('Aap.model.Endereco', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'logradouro', type: 'string'}
]});
data/data.json:
[
{
"id": 1,
"nome": "Loiane",
"sobrenome": "Groner",
"endereco": {
"id": 14,
"logradouro": "rua ficticia",
"numero": "100"
}
},
{
"id": 2,
"nome": "Tom",
"sobrenome": "Stock",
"endereco": {
"id": 34,
"logradouro": "reality street",
"numero": "55"
}
}]
Command in the browser console:
pessoaStore = new Ext.data.Store({
autoLoad: true,
model: 'Aap.model.Pessoa'
});
Here I get as a result the store with the data of the "Pessoa" model, but the "Endereco" model is not associated and I cannot get the corresponing data.
Any help is appreciated!
I figured out the problem:
In the "app/model/Pessoa.js file" I simply had to replace
hasOne: {model: 'Endereco', foreignKey: 'pessoaId'}
to
hasOne: {model: 'Aap.model.Endereco', associationKey: 'endereco', foreignKey: 'pessoaId'}
Now, the associaiton is working correctly.
The problem the associationKey, which is the property to read the association from. As explained in the ExtJS dcumentation the associationKey is by default the name of the associated moel.
In the first example (the one that worked), this worked well. The associationKey was correspondinly to the model name "Endereco" set to "endereco" by default.
In the second example (the one that did not work), however, the default associationKey did not find any property in the data (app/model/Pessoa.js) with the same name. The associationKey was correspondinly to the model name "Aap.model.Endereco" set to "aap.model.endereco" by default.
The associationKey had therefore to be defined explicitely as "endereco" in order to solve the problem.

Cannot access associeted model in ExtJS

I have two models in my ExtJS (4.2.1) application and would like to define a "hasOne" asscociation between the two models. However, I can't make it work.
I tryed to respect the rules for the "hasOne" association as indicated in http://extjs-tutorials.blogspot.ch/2012/05/extjs-belongsto-association-rules.html.
Main Model
Ext.define('Aap.model.TreeNode', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: "string"},
{ name: 'id'},
{ name: 'allgemein_id'}
],
proxy: {
type: 'ajax',
url: 'data/treedata.json',
reader: {
type: 'json',
root: 'nodes',
successProperty: 'success'
}
},
associations: [
{
type: 'hasOne',
model: 'Aap.model.Allgemein',
associationKey: 'metaallgemein',
primaryKey: 'id',
foreignKey: 'allgemein_id',
getterName: 'getAllgemein',
setterName: 'setAllgemein',
autoLoad: true
}
]
});
Associated model
Ext.define('Aap.model.Allgemein', {
extend: 'Ext.data.Model',
requires: 'Aap.model.TreeNode',
fields: [
{name: "name", type: 'string'},
{name: "allgemein_name", type: 'string'},
{name: "ident", type: 'string'},
{name: "georefdat", type: 'string'},
{name: "fachst", type: 'string'},
{name: "zugberech", type: 'string'},
{name: "echkateg", type: 'string'},
{name: "nachfzeitr", type: 'string'},
{name: "datenmenge"},
{name: "imjr"},
{name: "datenzuw"},
{name: "bemerk", type: 'string'},
{name: "treenode_id", type: 'int'},
{name: "id", type: "int"}
],
associations: [
{
type: 'belongsTo',
model: 'Aap.model.TreeNode'
}
],
proxy: {
type: 'ajax',
api: {
read: 'data/allgemeindata.json'
},
reader: {
type: 'json',
root: 'metaallgemein',
successProperty: 'success'
}
}
});
Data for main model
{
"success": true,
"nodes": [
{ "name": "st", "id":"1", "allgemein_id": "1", "expanded": false, "children": [
{ "name": "Geodätische Grundlagen", "id":"4", "allgemein_id": "4", "children": [
{ "name": "Beispiel Datensatz", "id":"6", "allgemein_id": "6", "leaf": true }
]},
{ "name": "Bilddaten", "id":"5", "allgemein_id": "5", "loaded": true}
]},
{ "name": "BU", "id":"2", "allgemein_id": "2", "loaded": true },
{ "name": "BE", "id":"3", "allgemein_id": "3", "loaded": true }
]
}
Data of associated model
{
"success": true,
"metaallgemein": [
{
"name":"st",
"ident": "stident",
"georefdat":"stgeorefdat",
"fachst":"stfach",
"zugberech":"stzugangsbe",
"echkateg":"stechkateg",
"nachfzeitr":"stzeitrnachf",
"datenmenge":"stdatenmende",
"imjr":"stimjr",
"datenzuw":"stdatenzuw",
"bemerk":"stbemerk",
"id": "1",
"treenode_id": "ext-record-1"
},
{
"name":"BU",
"ident": "buident",
"georefdat":"bugeorefdat",
"fachst":"bufach",
"zugberech":"buzugangsbe",
"echkateg":"buechkateg",
"nachfzeitr":"buzeitrnachf",
"datenmenge":"budatenmende",
"imjr":"buimjr",
"datenzuw":"budatenzuw",
"bemerk":"bubemerk",
"id": "2",
"treenode_id": "ext-record-2"
}, ...
...
...
When I load my page and try to get the associated data with my getter, the following happens (see image).
The first time I try to get the associated data (rec.getAllgemein()), I don not get
any data.
When I use the getter a second time (rec.getAllgemein()), I get data, but not the
correct ones. I added a Screenshot helping to explain the problem.
It seem to be a more fundametal problem. Some data are associated, but not correctly. Other record do not have any association.
I tried a simpler example in order to trace the problem. But I did not even achieve to make the association work in the most basic example. It seems that I make somewhere a fundametal mistake:
Ext.define('Aap.model.Person', {
extend: 'Ext.data.Model',
requires: 'Aap.model.Address',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'address_id', type: 'int'}
],
proxy: {
type: 'ajax',
api: {
read: 'data/persons.json'
},
reader: {
type: 'json',
root: 'children',
successProperty: 'success'
}
},
associations: [
{
type: 'hasOne',
model: 'Aap.model.Address'
}
]
});
Ext.define('Aap.model.Address', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int'},
{ name: 'city', type: 'string'}
],
proxy: {
type: 'ajax',
api: {
read: 'data/addresses.json'
},
reader: {
type: 'json',
root: 'children',
successProperty: 'success'
}
}
});
Ext.define('Aap.store.Persons', {
extend: 'Ext.data.Store',
model: 'Aap.model.Person',
autoLoad: 'true',
storeId: 'persons'
});
Ext.define('Aap.store.Addresses', {
extend: 'Ext.data.Store',
model: 'Aap.model.Address',
autoLoad: 'true',
storeId: 'addresses'
});
{
"success": true,
"children": [
{
"id": 1,
"name": "Bernt",
"address_id": 1
},
{
"id": 2,
"name": "Ludowid",
"address_id": 2
}
]
}
{
"success": true,
"children": [
{
"id": 1,
"city": "Berlin"
},
{
"id": 2,
"city": "London"
}
]
}
I don not know where the problem lies.
Is it the model that is not correctly defined?
Is there a problem with the data
Is it the model that is not correctly defined?
Or is it something completely different?
Blog entries such as:
ExtJS 4: Models with Associations and Stores
gave me a better understanding of how associations work. But they did'nt help me to solve the problem.
Thanks for help!
The generated getter should be used asynchronously, you can't rely on the actual return value.
// Don't do that
var unreliableResult = m.getAllgemein();
// Do that instead
m.getAllgemein(function(allgemeinRecord) {
// continue from here...
});
See the part named "Generated getter and setter" in the doc for more information.
Be warned however that the callback implementation seems a bit buggy.
Now, regarding the "wrong" data aspect of your question, your associations configuration seems correct to me. I quickly tested your code, and the correct request (that is, with the correct id for the associated record) was fired... So maybe you should elaborate why you think the data is "wrong", and someone may find the mistake then.

Sencha touch 2.0 many-to-many associations - how?

I've been having a hell of a time getting Sencha Touch 2.0 hasMany associations working, especially since it looks like their data models don't directly allow for many-to-many relationships. I've got two models - People and Roles (theere are a bunch more, but these are the two that matter in this example) , each has a many-to-many to the other.
I originally thought that I could do this with a hasMany in each of the models, but snce the data is stored in third-normal form in my db, I figure that I need a third, person-to-role model. Code is here:
Ext.define('SMToolkit.model.Person', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'first_name',
'last_name',
'email',
'address',
'phone1',
'phone2',
'type',
'location'
],
hasMany: [
{
model: 'SMToolkit.model.Person_Role',
name: 'role'
}
],
proxy: {
type: 'rest',
url : 'index.php/api/persons'
}
}
});
Ext.define('SMToolkit.model.Role', {
extend: 'Ext.data.Model',
config: {
fields: [
'id',
'name',
'description',
'type',
'show_id'
],
hasMany: [
{
model: 'SMToolkit.model.Person_Role',
name: 'person'
},
{
model: 'SMToolkit.model.Scene_Role',
name: 'scene'
},
{
model: 'SMToolkit.model.Thing',
name: 'thing'
}
],
proxy: {
type: 'rest',
url : 'index.php/api/roles'
}
}
});
Ext.define('SMToolkit.model.Person_Role', {
extend: 'Ext.data.Model',
config: {
fields: [
'person_id',
'role_id'
],
associations: [
{
type: 'belongsTo',
model: 'SMToolkit.model.Person',
name: 'person'
},
{
type: 'belongsTo',
model: 'SMToolkit.model.Role',
name: 'role'
},
],
proxy: {
type: 'rest',
url : 'index.php/api/personsroles'
}
}
});
I've confirmed that the personsroles url above does in fact return a valid data set, so I know that there should be something in there...
When I look at a Role record, I can see fields for the associated stores, but even if I know for certain that there's an appropriate record in the Person_Role table in the db, the Persons array in the record is empty.
I'm getting the record like so:
onRoleSelect: function(list, index, node, record) {
var editButton = this.getEditButton();
if (!this.showRole) {
this.showRole = Ext.create('SMToolkit.view.role.Show');
}
person = record.person();
thing = record.thing();
scene = record.scene();
person.load();
thing.load();
scene.load();
// Bind the record onto the view
this.showRole.setRecord(record);
// Push the show show view into the navigation view
this.getRoleContainer().push(this.showRole);
},
What am I doing wrong? Why is there no association data?
Here's an alternative approach for handling complex model relations in Sencha.
I've not yet tested it but I think it will likely handle Many-Many relations as well.
Recursive M-M relations might cause you grief with the linkChildAssociations() function.
http://appointsolutions.com/2012/07/using-model-associations-in-sencha-touch-2-and-ext-js-4/

Resources