I am working in a Sencha Touch app and with associated models
Parent model:
Ext.define('xx.model.TemplateModel', {
extend : 'Ext.data.Model',
requires : [
'xx.model.BrandsModel'
],
config : {
useCache : false,
idProperty: 'templates',
fields : [
{
name: 'id',
type: 'string'
},
{
name: 'name',
type: 'string'
},
{
name: 'validFrom',
type: 'auto'
},
{
name: 'validTo',
type: 'auto'
}
]
},
associations: [
{
type : 'hasMany',
associatedModel: 'xx.model.BrandsModel',
ownerModel : 'xx.model.TemplateModel',
autoLoad : true,
associationKey : 'brands'
}
]
});
and associated model:
Ext.define('xx.model.BrandsModel', {
extend : 'Ext.data.Model',
requires: [],
config : {
useCache : false,
idProperty: 'Brands',
fields : [
{
name: 'brandId',
type: 'string'
},
{
name: 'brandText',
type: 'string'
}
]
}
});
First point, this implementation is in the correct way?, because I cannot see this associated model when I am debugging in the chrome console.
Second point, What would it be the correct way to test this and to see the records?
Thank you.
I would have expected that it throws an error:
You have no "brands" field on your template model, but have specified the associationKey to be brands
You have not specified a primaryKey on the association although you use a modified idProperty on the BrandsModel
Also, please have a look at the example in the API docs how to lessen your code by using readable shortcuts.
Related
I've got an incoming array of Offering objects that looks like this:
[{
"id" : 16,
"price" : 500,
"quantity" : 2000,
"denomination" : "case",
"denominationPlural" : "cases",
"product" : {
"id" : 14,
"description" : "This is the text description for product 14."
}
}, {
"id" : 18,
"price" : 500,
"quantity" : 1,
"denomination" : "study",
"denominationPlural" : "studies",
"product" : {
"id" : 17,
"description" : "This is a text description for product 17."
}
}]
An Offering is a Product times a quantity for a price.
Now I want to display these Offerings in a GridPanel, but also include the nested product information in those rows. Here's how I was doing it before:
Ext.define('Offering', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'number'},
{name: 'price', type: 'number'},
{name: 'quantity', type: 'number'},
{name: 'denomination', type: 'string'},
{name: 'denominationPlural', type: 'string'},
{name: 'product.description', type: 'string'},
]
});
var offeringStore = Ext.create('Ext.data.Store', {
autoDestroy: true,
model: 'Offering',
proxy: {
type: 'ajax',
url: '/offerings',
reader: {
type: 'json',
root: 'success'
}
},
autoLoad:true
});
var offeringGrid = Ext.create('Ext.grid.Panel', {
store: offeringStore,
columns: [{
header: 'id',
dataIndex: 'id'
}, {
header: 'price',
dataIndex: 'price'
}, {
header: 'quantity',
dataIndex: 'quantity'
}, {
header: 'denomination',
dataIndex: 'denomination'
}, {
header: 'description',
dataIndex: 'product.description'
},
};
And this worked just fine. Then, somewhere along the way (which included an upgrade to ExtJS 5.1.1 (from ExtJS 4.2.1) and use of Sencha Architect, it broke.
Problem 1: Sencha Architect prevents creating an entry for "product.description" in the Offering Model, complaining about the "." character. But if you create it as "whateveryouwant", you can go into the model field and rename it to "product.description" there.
Problem 2: after working around the "." issue and running the application, the "product.description" column's cells are blank.
Problem 3: the javascript console produces zero errors. The incoming data looks fine.
How should I go about getting this nested data to show up?
The way I do it is quite simple. Just add the mapping attribute to the model this way:
{
name: 'product.description',
type: 'string',
mapping : 'product.description'
}
No need for renderer.
My current workaround, which may help others in the same situation, uses a "renderer" clause to display the information desired.
{
xtype: 'gridpanel',
title: 'Offerings',
bind: {
store: '{OfferingsStore}'
},
columns: [
...
{
xtype: 'gridcolumn',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
return record.getData().product.description;
},
text: 'Product Description'
},
...
]
But I'd still like an answer about using "product.description" in the Offering Model definition rather than this rendering trick.
How to properly use MVVM viewmodel store?
List.js:
Ext.define('some.List', {
extend: 'Ext.tree.Panel',
requires: [
'some.ListModel'
],
rootVisible : false,
hideHeaders: true,
viewModel: {
type: 'list'
},
bind: {store:'{mlists}'},
columns: [{
xtype: 'treecolumn',
dataIndex: 'name',
flex: 1,
sortable: false,
}]
});
ListModel.js:
Ext.define('some.ListModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.list',
requires: [
'Ext.data.proxy.Ajax'
],
stores: {
mlists: {
autoLoad: true,
fields: [
{ name: 'id', type: 'int'},
{ name: 'name', type: 'string' },
{ name: 'count' },
{ name: 'group' }
],
proxy: {
type: 'ajax',
api: {
read: 'php/lists/read.php'
},
reader: {
type: 'json',
rootProperty: 'lists'
}
}
}
}
});
i get error:
Uncaught TypeError: undefined is not a function Panel.js?_dc=1404313037482:430
Ext.define.bindStore Panel.js?_dc=1404313037482:430
Ext.define.reconfigure Table.js?_dc=1404313037482:1417
Ext.define.setStore Table.js?_dc=1404313037482:1376
Ext.define.privates.onBindNotify Bindable.js?_dc=1404313037482:681
Ext.define.privates.notify BaseBinding.js?_dc=1404313037482:83
Ext.define.privates.react Binding.js?_dc=1404313037482:206
Ext.define.notify Scheduler.js?_dc=1404313037482:394
Ext.define.onTick Scheduler.js?_dc=1404313037482:425
(anonymous function) Function.js?_dc=1404313037482:121
There are several issues with the code:
If it is Sencha Cmd generated then view someList should be in view directory. Of course it would work when it is elsewhere but there must be a good reason not to follow Sencha recommended directory structure. This is not related to the issue.
Sencha recommends to start namespace with a capital letter to distinguish from other variables. It should read Some, not some in this case. It is not related to the issue.
someList extends TreePanel so it must use a TreeStore. mlists is normal store. It can be cause of the issue.
If you want to define a tree store in ViewModel, configure it with type:'tree' config option and do not forget to configure also root option that is mandatory for tree stores.
Then you can bind it normally as any other store the way you are already using.
I'm having trouble trying to invoke getters/setters on a Model object that has an association with one other model. Here are the classes:
Category.js
Ext.define('Chapter05.model.Category', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int' },
{ name: 'name', type: 'string' }
]
})
Product.js
Ext.define('Chapter05.model.Product', {
extend: 'Ext.data.Model',
requires: [
'Chapter05.model.Category'
],
fields: [
{ name: 'id', type: 'int' },
{ name: 'category_id', type: 'int' },
{ name: 'name', type: 'string' }
],
// we can use the belongsTo shortcut on the model to create a belongsTo association
associations: [
{ type: 'belongsTo', model: 'Chapter05.model.Category' }
]
})
Main.js
Ext.define('Chapter05.view.Main', {
extend: 'Ext.container.Container',
requires:[
'Ext.tab.Panel'
'Chapter05.model.Product',
'Chapter05.model.Category',
],
xtype: 'app-main',
layout: 'vbox',
items: [
{
xtype: 'button',
text: 'Category',
handler: function(evt) {
var product = new Chapter05.model.Product({
id: 100,
category_id: 20,
name: 'Sneakers'
});
product.getCategory(function(category, operation) {
// do something with the category object
alert(category.get('id')); // alerts 20
}, this);
}
}
]
});
The error occurs at the line where product.getCategory(...) is. I get the following message in Safari Web Inspector:
TypeError: 'undefined' is not a function (evaluating 'product.getCategory')
Am I forgetting to do something?
P.S.
The project(Chapter05) was generated using Sencha Cmd. Hence, the fully qualified names.
I've had a similar problem with a hasOne relation. It was solved by specifying the getters/setters and the associationKey yourself.
Something like:
belongsTo: {
model: 'Chapter05.model.Category',
getterName: 'getCategory',
setterName: 'setCategory',
associationKey: 'category_id'
}
I'm using Ext 4.1. I have some issues with loading json data with associations. The flat data gets loaded perfectly, only the 'hasMany' doesn't work. (if loaded there nowhere to be found). If a record is loaded I want to be able to get the 2 stores of Attendee's and the 1 store of documents.
I can also change the JSON format to a better format (if you have suggestions let me know!)
I have this json data.
This is my first model:
Ext.define('App.model.package.LabVisit', {
extend: 'Ext.data.Model',
requires: [
'App.model.package.Attendee',
'App.model.package.Document'
],
idProperty: 'labVisitID',
fields: [
{
mapping: 'lab_visit_id',
name: 'labVisitID',
type: 'int'
},
{
mapping: 'lab_id',
name: 'labID',
type: 'int'
},
... some more irrelevant...
{
mapping: 'comments',
name: 'comments'
},
{
name: 'upddate'
}
],
hasMany: [
/* edit: added foreignKey (also tried with: lab_visit_id) */
{ model: 'package.Attendee', name: 'attendeeLabList', associationKey:'attendee_lab', foreignKey: 'labVisitId' },
{ model: 'package.Attendee', name: 'attendeeEmpList', associationKey:'attendee_emp', foreignKey: 'labVisitId' }
{ model: 'package.Document', name: 'document', associationKey:'document' },
]
});
I have following attendee model:
Ext.define('App.model.package.Attendee', {
extend: 'Ext.data.Model',
fields: [
/* edit: added this field */
{
mapping: 'lab_visit_id',
name: 'labVisitId'
},
{
mapping: 'attendee_id',
name: 'AttendeeID'
},
{
mapping: 'first_name',
name: 'firstName'
},
{
mapping: 'last_name',
name: 'lastName'
},
{
name: 'email'
}
]
});
following document model:
Ext.define('App.model.package.Document', {
extend: 'Ext.data.Model',
fields: [
{
mapping: 'document_id',
name: 'docID'
},
{
mapping: 'document_name',
name: 'docName'
},
{
mapping: 'document_mimetype',
name: 'mimeType'
},
{
name: 'uploadID'
}
]
});
Finally my store:
Ext.define('App.store.package.LabVisit', {
extend: 'Ext.data.JsonStore',
requires: [
'App.model.package.LabVisit'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.merge({
storeId: 'labVisitStore',
model: 'App.model.package.LabVisit',
remoteSort: true,
proxy: {
type: 'ajax',
api: {
read: API_URLS.getVisitList //url to the json
},
reader: {
type: 'json',
root: 'rows'
}
}
}, cfg)]);
}
});
Edit:
I've added the foreign key in the model and added it to the hasMany
Still no difference. This is my output:
I also find it a strange: If it's broken I expect an exception. And there are 2 mysterious stores always present but I don't have a clue why or what's the purpose.
The problem was in this part:
hasMany: [
{ model: 'package.Attendee', name: 'attendeeLabList', associationKey:'attendee_lab' },
{ model: 'package.Attendee', name: 'attendeeEmpList', associationKey:'attendee_emp' },
{ model: 'package.Document', name: 'document', associationKey:'document' },
]
#Izhaki helped me a lot. Thanx! Especially with the fiddle. I started there and begun with switching the code with my code piece by piece. Until I saw the model was the problem.
models should be defined like this: App.model.package.Attendee
I think it's sad that the framework doesn't show a significant error/warning if a model isn't recognised/doesn't excist/isn't supplied... But meh, it works now.
I'm teaching myself ExtJS by building a really simple 'scrum' development tracking application. I'm currently displaying the "Backlog" as a grid panel that displays the properties of the card(user story).
Card.js (Card model)
Ext.define('AM.model.Card', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'priority_id',
...
]
});
Priority.js (Priority model)
Ext.define('AM.model.Priority', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'short_name'
]
});
So the data for the card will look something like this:
backlogcards.json (data)
{
success: true,
backlogcards: [
{
id: 1,
name: 'ONEs',
priority_id: 2,
...
},
{
id: 2,
name: 'TWOs',
priority_id: 3,
...
}
]
}
And the priorities looks like this:
priorities.json (data)
{
success: true,
priorities: [
{
id : 1,
name : "High",
short_name : "H"
},
{
id : 2,
name : "Medium",
short_name : "M"
},
{
id : 3,
name : "Low",
short_name : "L"
}
]
}
So ideally what I would like to do is have the grid panel display the short_name for the corresponding priority_id. When the item is clicked on to be edited inline, a combo box will be displayed that shows the name property. I'm half way there already.
BacklogList.js (view)
Ext.define('AM.view.card.BacklogList', {
extend: 'Ext.grid.Panel',
alias: 'widget.backlogcardlist',
title: 'Backlog',
store: 'BacklogCards',
selType: 'cellmodel',
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
columns: [
{ header: 'ID', dataIndex: 'id' },
{ header: 'Name', dataIndex: 'name', field: 'textfield' },
{
header: 'Priority',
dataIndex: 'priority_id',
width: 130,
field: {
xtype: 'combobox',
typeAhead: true,
store: 'Priorities',
displayField: 'name',
valueField: 'id',
listClass: 'x-combo-list-small'
}
}
]
});
So I know the 'dataIndex' property is what I need to modify in order to change the display, but I'm not sure how to tie those two stores together.
As you can see above, priority is being displayed as a number instead of the short_name.
Is this a situation where I would need to use associations? (I only know OF them) Sencha Docs
Thank you!
EDIT1: Oh I realize I could 'hard code' a renderer property that does this change, but I would like to avoid that and instead use values from the priorities store.
renderer: function(value){
if (value==3)
{
return "L";
}
else if (value==2)
{
return "M";
}
else
{
return "H";
}
},
EDIT2 for Evan:
Priorities store
Ext.define('AM.store.Priorities', {
extend: 'Ext.data.Store',
model: 'AM.model.Priority',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'app/data/priorities.json',
update: 'app/data/updateUsers.json'
},
reader: {
type: 'json',
root: 'priorities',
successProperty: 'success'
}
}
});
The store.each refers to this store, right? If so, how do I perform the each operation on it?
I tried changing the declaration line to:
var test = Ext.define('AM.store.Priorities', {
And then tried changing your code to test.each but was unsuccessful.
Thanks again!
You need to use a renderer, however there's nothing stopping you from looping over the values in the priorities store and checking, something like:
renderer: function(value) {
var display = '';
store.each(function(rec){
if (rec.get('id') === value) {
display = rec.get('name');
return false;
}
});
return display;
}