Associations and class names - extjs

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.

Related

Need help setting up hasMany model relationship for nested JSON and showing data in grid - EXTJS 6

I am getting some search results back from Elastic in this format
{
"took": 267,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1911,
"max_score": 29.118078,
"hits": [
{
"_index": "myIndex",
"_type": "doc",
"_id": "27c9d14495f7732c6756f0d2688874f",
"_score": 21.179974,
"_source": {
"file": {
"filename": "file1.pdf"
},
"content": "samplecontent"
}
},
{
"_index": "myIndex",
"_type": "doc",
"_id": "27c9d14495f7732c6756f0d2688874f",
"_score": 21.179974,
"_source": {
"file": {
"filename": "file2.pdf"
},
"content": "samplecontent"
}
},
{
"_index": "myIndex",
"_type": "doc",
"_id": "27c9d14495f7732c6756f0d2688874f",
"_score": 21.179974,
"_source": {
"file": {
"filename": "file3.pdf"
},
"content": "samplecontent"
}
}
]
}
}
and would like to display the hits.total (1911) in the header of my grid for example, and hits.hits._source.file.filename and hits.hits._source.content in my grid. I believe I need to setup a hasMany/belongsTo model but am a bit confused by any documentation/examples that I've found as to how to setup that relationship, as well as pick and choose those nested fields to display in the grid. Any help or clarification would be appreciated
My store is defined as
Ext.define('search.store.results', {
extend: 'Ext.data.Store',
alias: 'store.results',
model: 'search.model.hits',
autoLoad: false,
proxy: {
type: 'ajax',
url: 'http://myServer:9200/_search',
reader: {
type: 'json',
rootProperty: 'hits'
}
}
});
And my models are defined as
base
Ext.define('search.model.Base', {
extend: 'Ext.data.Model',
schema: {
namespace: 'search.model'
}
});
hits
Ext.define('search.model.hits', {
extend: 'search.model.Base',
fields: [
{name: 'total', type: 'number'}
],
hasMany:{
model: 'hit',
name: 'hit'
}
});
hit
Ext.define('search.model.hit', {
extend: 'search.model.Base',
fields: [
{name: '_source', type: 'source'}
]
});
source
Ext.define('search.model.source', {
extend: 'search.model.Base',
fields: [
{name: 'file', type: 'file'},
{name: 'content', type: 'string'}
]
});
and file
Ext.define('search.model.file', {
extend: 'search.model.Base',
fields: [
{name: 'filename', type: 'string'}
]
});
however when I load my page I get these errors right off the bat
Uncaught Error: [Ext.createByAlias] Unrecognized alias: data.field.source
and
Uncaught Error: [Ext.createByAlias] Unrecognized alias: data.field.file
UPDATE: This is what the results look like if I change my models to use 'reference' instead of hasMany
Ext.define('search.model.hits', {
extend: 'search.model.Base',
fields: [
{name: 'total', type: 'number'},
{name: 'hits', reference: 'hit'}
]
});
Ext.define('search.model.hit', {
extend: 'search.model.Base',
fields: [
{name: '_source', reference: 'source'}
]
});
Ext.define('search.model.source', {
extend: 'search.model.Base',
fields: [
{name: 'file', reference: 'file'},
{name: 'content', type: 'string'}
]
});
Ext.define('search.model.file', {
extend: 'search.model.Base',
fields: [
{name: 'filename', type: 'string'}
]
});
My store 'load' listener shows this information, broken into separate data structures instead of all being under the 'data' structure where the arrow is (where I'm used to finding my store records)
UPDATE #2:
If I modify my 'hits' model to use hasMany but leave the rest using 'reference' it appears to work as far as grouping my data properly in the records - now I just need to know how to display the nested results in a grid
Ext.define('search.model.hits', {
extend: 'search.model.Base',
fields: [
{name: 'total', type: 'number'}
],
hasMany:{
model: 'hit',
name: 'hit'
}
});
produces

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.

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.

ExtJS 4.1 - Nested hasOne associated in JSON

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

Resources