I am having some trouble when I create a new model form json, with nested attributes that should be parsed as a collection. Here I show how I get to the error:
(Any help appreciated)
1) I create a user like this
user = new App.Models.User( {"email":"tom#gmail.com","id":1,"user_cities_attributes":[{"city_id":431,"user_action":"to-visit","user_id":1}] }
2) and then I check in the console what happened, the collection did not get created. :S
user.get('user_cities')
> undefined # where is my collection?! :(
user.get('user_cities_attributes')
> [Object] # mmm weird...
If I try after to add a new model to the collection...
cities = new App.Collections.CitiesCollection()
cities.fetch()
userCity = new App.Models.UserCity( { user: user, city: cities.first(), user_action: "to-visit" } )
now everything seems to be fine... what happened????
user.get('user_cities')
> Backbone.Collection {length: 20, models: Array[20], _byId: Object, model: function, user: User…}
user.get('user_cities_attributes')
undefined
MODELS
class App.Models.User extends Backbone.RelationalModel
paramRoot: 'user'
urlRoot: 'users'
defaults:
name: null
class App.Models.UserCity extends Backbone.RelationalModel
paramRoot: 'user_city'
urlRoot: 'user_cities'
defaults:
user_action: null
relations: [{
type: Backbone.HasOne,
key: 'user',
relatedModel: 'App.Models.User',
collectionType: 'App.Collections.UsersCollection',
keySource: 'user_id',
includeInJSON: 'id',
reverseRelation: {
key: 'user_cities',
keySource: 'user_cities_attributes',
includeInJSON: 'id'
}
},
{
type: Backbone.HasOne,
key: 'city',
relatedModel: 'App.Models.City',
collectionType: 'App.Collections.CitiesCollection',
includeInJSON: 'id',
keySource: 'city_id',
reverseRelation: {
key: 'users',
includeInJSON: 'id'
}
}]
class App.Models.City extends Backbone.RelationalModel
paramRoot: 'city'
urlRoot: 'cities'
defaults:
name: null
relations: [{
type: Backbone.HasMany,
key: 'places',
relatedModel: 'App.Models.Place',
collectionType: 'App.Collections.PlacesCollection',
keySource: 'places_attributes',
# parse: true,
includeInJSON: 'id',
autoFetch: true,
reverseRelation: {
key: 'city',
includeInJSON: 'id'
}
# 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
}]
Look at the note about reverseRelation and using coffeescript (from: http://backbonerelational.org/#relations-reverseRelation):
Note that if you define a relation (plus a reverseRelation) on a
model, but don't actually create an instance of that model, it is
possible initializeRelations will never get called, and the
reverseRelation will not be initialized. This can happen when extend
has been overridden, or redefined as in CoffeeScript. See setup.
Then try using .setup as described here: http://backbonerelational.org/#RelationalModel-setup
Related
edit http://jsfiddle.net/zns6B/4/ Added js fiddle link and running into a cannot get 'Fields' of undefined now
edit2 So i found that the second grids store model is correct with Sc.Model.B. But the records in the store have ids that are Sc.Model.A . So my store model is set to Sc.Model.B but the store is using Sc.Model.A . It still gets stores the data in the store but only as if the model was set to Sc.Model.A in the first place.
edit3 I have take all the creation of instance out of my ListGrid. I have instead added them when creating the list grid. I have added the following. This does not work either. I am at a lose for what to do.
var obj1 = Ext.create('Sc.ListGrid',{
title: "first Component",
foo: true,
id: 'firstGrid',
myStore: Ext.create('My.Store.MyStore',{model:Ext.create('My.Model.Model'});
renderTo: 'renderToMe1'
});
I am trying to generate these two grids. When foo == true i want it to generate a store with model A. When it equals false i want it to use model B. I have tried to just specifically add the My.Model.MyModel but that does not work. The second grid will somehow inherit the first models model. I have changed it just to try and use fields instead of using the model at all but the second grid still uses the first grids.
I have also tried declaring the Stores inside the initComponent as well but i get the same result either way.
var obj1 = Ext.create('Sc.ListGrid',{
title: "first Component",
foo: true,
id: 'firstGrid',
renderTo: 'renderToMe1'
});
var obj2 = Ext.create('Sc.ListGrid',{
title: "second Component",
foo: false,
renderTo: 'renderToMe2'
});
Sc.ListGrid
Ext.define('Sc.ListGrid', {
extend: 'Ext.grid.Panel',
title: 'Grid',
store: Ext.data.StoreManager.lookup('bleh'),
requires: ['stuff'],
columns: [
{ text: 'id', dataIndex: 'id' },
],
config:{
foo: null,
},
initComponent: function(){
if(this.foo == true){
Ext.apply(this,{
store: this.buildStore1()
});
}
if(this.foo == false){
Ext.apply(this,{
store: this.buildStore2()
});
}
this.callParent();
},
buildStore1:function(){
return Ext.create('Sc.Store.League.LeagueStore',{url:'somewhere',fields:["S"]});
},
buildStore2:function(){
return Ext.create('Sc.Store.League.LeagueStore',{url:'somewhere',fields:["A"]});
}
});
Also an example of a model i am trying to use as well.
Ext.define('Sc.Model.A', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'gamename', type: 'string'}
]
});
Ext.define('Sc.Model.B', {
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'string'},
{name: 'type', type: 'string'},
{name: 'gamename', type: 'string'},
{name: 'something1', type: 'string'},
{name: 'something2', type: 'string'},
]
});
It will create both grids and load the data from my webservice. When i check the grid with Sc.Model.B's data it will have id and type. But will not have any data for something1, and something2. My webserivce is returning json and all values are entered. There are no nulls. If i Ext.getCmp('firstGrid').getStore().getData(0); If i use Ext.getCmp('firstGrid').getStore() and check the model name. It shows Model B but reflects A
Do you need it to be done in the initComponent()??
This is a fiddle I saved from a while ago when I was trying to do something similar. If you need help tweaking it let me know and ill update it.
The main thing to note is the grid.reconfigure(store,columns);
That will change the grid's store and columns appropriately.
http://jsfiddle.net/zqG55/1/
The issue was that the proxy wasn't being set or created properly because the proxy model was referencing the previous model instance. This is my solution
var themodel = 'A.Model.SomeModel';
var myProxy = new Ext.data.proxy.Ajax({
model: themodel,
url: url,
reader: {
type: 'json',
}
});
Ext.apply(this,{
columns: modelColumns.columns,
store: Ext.create('M.Store.MyStore',{
model: themodel ,
autoLoad: true,
proxy: myProxy
})
});
I have a Person model which has an Address associated to it via 'hasOne'. Both models load data via REST proxy. If I load a person and try to get the address I can see a http request to the url of the addresses but without any kind of person id in it so my server sends an error.
Person:
Ext.define('AP.model.Person', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'int', persist : false },
{ name: 'name', type: 'string' }
],
proxy: {
type: 'rest',
url : AP_ROOT_URL + 'persons/'
},
associations: [{
type: 'hasOne',
model: 'AP.model.Addresses',
foreignKey: 'person_id',
primaryKey: 'id',
getterName: "getAddress"
}]
});
Address:
Ext.define('AP.model.Address', {
extend: 'Ext.data.Model',
fields: [
{ name: 'person_id', type: 'int', persist : false },
{ name: 'address', type: 'string' }
],
proxy: {
type: 'rest',
url : AP_ROOT_URL + 'addresses/'
}
});
The following code:
person.getAddress(function(address){
console.log(address);
});
creates a request to:
http://localhost/addresses
when it should create a request to:
http://localhost/addresses/person_id (http://localhost/addresses/1)
What you have here is a HasMany relation, not HasOne. You won't get HasOne to work with these models because, as said in the docs: "the owner model is expected to have a foreign key which references the primary key of the associated model". In your case, the foreign key is in the associated model (which makes it a 1-n relationship).
Based on some examples of Ext.data.Model with associations I wrote the following class:
Ext.define('MyApp.model.Children',{
extend: 'Ext.data.Model',
fields : [{
name: 'parent' //object of the belongsTo
},{
name: 'description',
type: 'string'
}],
belongsTo : [{
name: 'parent',
foreignKey: 'parent', //also tried parent.id
instanceName: 'parent',
getterName: 'getParent',
model: 'MyApp.model.Parent'
}],
proxy : {
type: 'rest',
url: '../rest/children',
reader : {
type: 'json',
root: 'data'
}
}
});
Shouldn't this definition generate a getChildren method? The MyApp.model.Parent also have a proxy defined.
I'm testing with:
var store = Ext.create('Ext.data.Store',{
model: 'MyApp.model.Children'
});
store.load(function(recs){
console.log(recs[0].getParent); //prints undefined instead of function
});
It looks like your parent model isn't loaded. Try adding the requires configuration to your child model.
Ext.define('MyApp.model.Children', {
extend: 'Ext.data.Model',
//ensures the Parent model is loaded first
requires: 'MyApp.model.Parent',
fields: ...
How can i fetch a record using belongs to association ?
var Category = Ext.regModel('Category', {
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'}
]
});
var Product = Ext.regModel('Product', {
fields: [
{name: 'id', type: 'int'},
{name: 'category_id', type: 'int'},
{name: 'name', type: 'string'}
],
associations: [
{type: 'belongsTo', model: 'Category'}
]
});
Amol,
I speak only of ExtJS 4, so if you can relate this to ExtJS 3 then great, but you would retrieve multiple records via a store:
As a starting point though you could do
var records = Ext.StoreManager.lookupStore("productStore").load({
belongTo: 'books',
callback: function(records) {
Ext.each(records,function(record) {
console.log(JSON.stringify(record.raw))
});
}
});
Depending on the proxy setup on the productStore, this will append belongTo to the querystring so if reader proxy has url: /products and the extra params belongTo is sent this will hit the server with /products?belongTo=books
I just downloaded the final version of ExtJs 4 and I'm trying to implement some things using new Model approach.
For instance I have a model named SetupModel, it has 2 nested models Users, Reports.
I create new store and I set the Model property of the store = SetupModel.
The question is - how can I access my nested properties after data was loaded into the store?
I need something like myStore.data.Users(), but is incorrect.
Any thoughts?
When you define your model, you need to provide the necessary association with the nested models. Since, you have not provided your code. Here is an example:
My Product Model:
Product = Ext.define('Product',{
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'user_id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'price', type: 'float'}
],
proxy: {
type: 'localstorage',
id: 'products'
}
});
My User Model:
User = Ext.define('User',{
extend: 'Ext.data.Model',
fields: [
{name: 'id', type: 'int'},
{name: 'name', type: 'string'},
{name: 'gender', type: 'string'},
{name: 'username', type: 'string'}
],
associations: [
{type: 'hasMany', model: 'Product', name: 'products'}
],
proxy: {
type: 'localstorage',
id : 'users'
}
});
Now, if you have a instance of User model with products. Here is how you can access the products:
var productStore = user.products();
Note that user.products() returns a Ext.data.Store. Now, you can traverse or filter or find your product record. Here is how I got my first product's name:
productStore.getAt(0).get('name');