I thought backbone-relational automatically parses and makes nested models ready of nested json. I have a big json like this
{ //ItemResultModel
"items":[
{ //ItemModel
"id":120514,
"recordDate":"2013-10-19T00:00:00",
"owner":{
"id":"d14a052b-a9df-45ba-92e5-58adfe28c10c",
"firstName":null,
"lastName":null
},
"features":[
{ //FeatureModel
"id":1,
"properties":[
{ //PropertyModel
"id":814518,
"values":[
"5"
]
}
//,other properties
]
}
//, other features
]
}
],
//other models and collections
"facets":{
},
"totalCount":7
}
And i tried to parse this json to my models by using backbone relational. And here are my models:
app.Models.ItemResultModel = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'items',
relatedModel: 'app.Models.ItemModel',
collectionType: 'app.Collections.ItemCollection',
parse: true
}]
});
app.Collections.ItemCollection = Backbone.Collection.extend({
model: app.Models.ItemModel
});
app.Models.ItemModel = Backbone.RelationalModel.extend({
urlRoot: '/api/Item',
relations: [{
type: Backbone.HasMany,
key: 'features',
relatedModel: 'app.Models.FeatureModel',
collectionType: 'app.Collections.FeatureCollection',
parse: true
}]
});
app.Models.FeatureModel = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'properties',
relatedModel: 'app.Models.ItemPropertyModel',
collectionType: 'app.Collections.ItemPropertyCollection'
}]
});
app.Collections.FeatureCollection = Backbone.Collection.extend({
model:app.Models.FeatureModel
});
and the same schema goes on for properties and deeper levels. The problem is, when i fetch the ItemResultModel from server i don't get my models populated after second level, i mean, i don't get features models.
this.model.get('items'); // this has some item models as i expect
this.model.get('items').at(0).get('features');//items does not have any feature model
How can i make my models ready after fetching the wrapper model? If you offer some solution without using backbone-relational, by using parse method, it is ok too.
if you use simple backbone model?
this.model.get('items')[0].features
or if you create collection based on items array:
this.collection.at(0).get('features')
Now I realized there are similar question. Sorry for this.
No need to use backbone-relational for this.
app.Models.ItemResultModel = Backbone.Model.extend({
subCollections: {
items: app.Collections.ItemCollection
},
parse: function (response) {
//Solution
this.set({
itemsCollection: new app.Collections.ItemCollection(response.items, { parse: true })
});
delete response.items;
//Better solution
for (var key in this.subCollections) {
var embeddedClass = this.subCollections[key];
var embeddedData = response[key];
response[key] = new embeddedClass(embeddedData, { parse: true });
}
return response;
}
})
And you can do the same thing for all levels of hierarchy.
Related
demoAlerts({
itemstatus: [
{
item: "1",
name: "apple",
type: "fruit"
},
{
item: "2",
name: "pine",
type: "fruit"
}
],
deliverystatus: [
{
name: "james",
status: "yes"
},
{
name: "thomas",
status: "no"
},
]
});
I have two array (itemstatus and deliverystatus), I need to create the model for this store. what I tried is
ParentModel:
Ext.define('test.model.ParentModel', {
extend: 'Ext.data.Model',
requires:['test.model.ItemModel','test.model.DeliveryModel'],
autoLoad: true,
config : {
fields : [ 'itemstatus', {
name : 'demostatuslist',
model : 'demoAlerts.model.ItemModel',
mapping : 'itemstatus'
},
'portalstatus', {
name : 'deliverystatus',
model : 'test.model.DeliveryModel',
mapping : ' deliverystatus'
}]
}
});
ItemModel
Ext.define('demoAlerts.model.ItemModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'item', type: 'string' },
{ name: 'name', type: 'string' },
{ name: 'type', type: 'string' }
]
}
});
DeliveryModel
Ext.define('demoAlerts.model.DeliveryModel', {
extend: 'Ext.data.Model',
config: {
fields: [
{ name: 'name', type: 'string' },
{ name: 'status', type: 'string' },
]
}
});
Whether i properly configured the model. I am receiving the store as empty
Use Associations :) http://docs.sencha.com/touch/2.3.1/#!/api/Ext.data.association.Association
In this case I would have a Model with 2 hasMany associations like this:
Ext.define('demoAlerts.model.ContainerModel', {
extend : 'Ext.data.Model',
requires : [
'demoAlerts.model.DeliveryModel',
'demoAlerts.model.ItemModel'
],
config : {
associations: [
{
type : 'hasMany',
model : 'demoAlerts.model.DeliveryModel',
associationKey : 'deliveryStatus',
name : 'deliveryStatus',
autoLoad : true // this is very important
},
{
type : 'hasMany',
model : 'demoAlerts.model.ItemModel',
associationKey : 'itemStatus',
name : 'itemStatus',
autoLoad : true // this is very important
}
]
}
});
Then use a store SomeStore binded to ContainerModel.
IMPORTANT Each record in SomeStore will have deliveryStatusStore and itemStatusStore AUTOGENERATED.
Read about associations.
Neither http://docs.sencha.com/touch/2.3.1/#!/api/Ext.data.Field
nor http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.field.Field
knows a valid config option "model" for a field.
As far as I know, there is no such thing as a "Parent Model" available in ExtJS or Sencha Touch.
As far as I know, there is no possibility to have two stores in one.
You can load two (or more) stores using only one call to the server like in my following example:
firststore.on('load',function() {
secondstore.loadRawData(firststore.getProxy().getReader().rawData);
});
firststore.load()
Where you would give firststore the server url and the root of the data that goes into the first store, and secondstore will have the root of the data that goes into the second store.
Please be aware that the second store won't be filled if zero records go into the first store, and choose your stores appropriately.
If any of your stores can be the only empty store, perhaps you will have to get the rawdata via Ext.data.Request and load it into all stores afterwards.
I asked an ExtJS question a few days ago, and as a side note I also asked how I could connect my two models. Main answer got answered, but I still couldn't figure out my other problem, so I am opening a new question for it.
It might be a silly problem again, but here it is:
I get a JSON from the server, that looks like this:
{
"success": true,
"result": {
"publishers": [
{
"id": "009999",
"type": "ABC",
"isReceipient": false,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": true,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": false,
"description": ""
}
],
"notes": [
{
"publisherId": "009999",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": "46",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": 45,
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
}
]
}
}
So I get two arrays, publishers and notes. I have two model for that, I load them in the models by the controller using loadRawData(), it works, I got all my publishers and notes in the store. (They both have a store - Publishers and Notes). But then I need to use the publisherId in notes to display publishers description. I tried a lot of things I could find using google and sancha docs: associations, hasmany, hasone, belongsto and creating a third store consisting of the two aggregated model. Nothing worked so far.
What I want is to have a store that has every notes, plus all notes have the publisher info.
I'll copy my two models below, you can see there, commented out what I have been trying. I also tried changing ID's, names etc., so variations of these. But I could never get the Notes to have the publisher's info.
Ext.define('MA.model.Note', {
extend: 'Ext.data.Model',
fields: [
'publisherId',
'text' ,
//hasone publisher
{
name: 'created',
type: 'date',
dateFormat: 'c'//'d-M-Y H:i:s' //"2014-02-23T18:24:06.074Z" needed: "02/23 18:24"
}
]
// hasOne: [
// {
// name: 'publisher',
// model: 'Publisher',
// associationKey: 'publisherId'
// }
// ],
// associations: [
// {
// type: 'hasOne',
// model: 'Publisher',
// primaryKey: 'id',
// foreignKey: 'publisherId'
// }
// ]
// associations : [
// {
// type : 'hasOne',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'User',
// associationKey : 'User'
// },
// {
// type : 'belongsTo',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'Publisher',
// associationKey : 'publisherId'
// }
// ]
// belongsTo: [
// {
// model: 'MA.model.Publisher',
// name: 'Note',
// primaryKey: 'publisherId',
// foreignKey: 'id',
// // foreignStore: 'Publishers'
// }
// ]
});
Publisher:
Ext.define('MA.model.Publisher', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
'id',
'type' ,
{
name:'isReceipient',
type:'boolean'
},
'description'
]
// hasMany: [
// {
// model: 'MA.model.Note',
// name: 'Note',
// primaryKey: 'id',
// foreignKey: 'publisherId',
// // foreignStore: 'Notes'
// }
// ],
});
Am I even on the right track? Should I use associations? I couldn't even really get the difference between associations and hasMan/One/belongTo properties, I guess there isn't any really, just the way you declare it.
Edit: My idea is to have a DataView class, that has a store which holds the notes and the corresponding publisher to the notes. I have a main panel:
items: [
{
xtype: 'create-note-panel',
flex: 1
},
{
xtype: 'notes-panel',
store: 'Notes',
flex: 1
}
]
And my notes-panel looks something like this:
Ext.define('MA.view.sections.notes.NotesPanel' ,{
extend: 'Ext.DataView',
alias: 'widget.notes-panel',
// deferInitialRefresh: true,
itemSelector: 'div.notes-list',
tpl: new Ext.XTemplate(
'<div class="notes-list">',
'<tpl for=".">',
'<p>{created}, by {publisherId}</p>',
'<p>{text}</p>',
'<hr />',
'</tpl>',
'</div>'
),
emptyText: 'No data available',
initComponent: function() {
var me = this,
publisherStore = Ext.data.StoreManager.lookup('Publishers');
//me.addEvents( //just messing here, trying stuff
// 'user-offer-activities'
//);
me.callParent(arguments);
}
//renderTo: Ext.getBody()
})
;
Notice the publisherId in the template. I need the publisher description there. I didn't want to use grid, as this DataView seemed a pretty good solution, I thought joining two stores would be easy, I just couldn't figure it out yet :(
I have created a fiddle that results in what you are after (displaying data from both models in the View).
It is a bit of a longwinded approach though, because of the way the tpl works you don't have access to the Model, just the data within it. So I created a function on the tpl that gets the record we're interested in from the model based on the publisherId.
https://fiddle.sencha.com/#fiddle/o9o
Note: I created the fiddle without using any associations between the models, but you could probably create a hasOne association from the Notes to Publisher with a foreignKey linking the id and publisherId in the respective models (though I still don't think this would enable you to refer to the members directly in the tpl).
I need to display report in Grid / Table in Sencha Touch 2.3.0. Is there any build in function to do so.
The store needs to populate data from a REST webservice call.
For loading data you can use Ext.data.Store configured with REST proxy Ext.data.proxy.Rest
For displaying data from store you can use Ext.grid.Grid of Ext.dataview.DataView
I do this all the time with something like this:
(Though I haven't worked with the Grid, I'm quite sure the same principles apply...)
Ext.define('App.controller.GridController', {
extend : 'Ext.app.Controller',
config: {
refs: {
getApiButton: 'button[action=getApiData]'
},
control: {
'getApiButton' : {
tap : 'onButtonTap'
}
}
},
onButtonTap : function(field, value) {
var that = this;
Ext.Ajax.request({
url : someWebServiceUrl,
method: 'GET',
success: function (result, request) {
var res = Ext.decode(result.responseText);
if (res.success === true && res.data != false) {
var recipient = {
name: res.data[0].displayname,
picId: res.data[0].pictureid,
gender: res.data[0].gender
};
var mod = Ext.define('App.model.GradingPopModel', {
extend: 'Ext.data.Model',
config: {
fields: [
'name',
'picId',
'gender'
]
}
});
/* == this is probably where you want to make your changes to apply the model to the grid template
the sencha website has this:
data: {'items': [
{ 'name': 'Lisa', "email":"lisa#simpsons.com", "phone":"555-111-1224" },
{ 'name': 'Bart', "email":"bart#simpsons.com", "phone":"555-222-1234" },
{ 'name': 'Homer', "email":"home#simpsons.com", "phone":"555-222-1244" },
{ 'name': 'Marge', "email":"marge#simpsons.com", "phone":"555-222-1254" }
]}
*/
var store = Ext.create('Ext.data.Store', {
model: mod,
storeId:'recipStore'
});
store.add(recipient);
store.setData(recipient);
store.load();
var gridView = Ext.ComponentQuery.query('#gridViewId')[0];
gridView.setStore(store);
}
},
failure: function (result, request) {
console.log('api call was a failure');
},
scope: this
});
}
});
Now of course your data won't have "displayname, gender" etc.. but you should get the point.
I also found a working example here (I find it very sad that Sencha allows broken demos on their own website): http://demo.rasc.ch/eds/touch23/grid/grid/index.html
Im now studying Sencha Touch 2 and doing some Research on Ext.data.LocalStorage that can be use in Offline Mode.
I tried to follow this turorial
Sencha Touch 2 Local Storage
and just updated the code from Github - RobK/SenchaTouch2-LocalStorageExample or riyaadmiller/LocalStorage and modified Store url using my own WCF Rest
but i cant get LocalStorage working on offline mode.I have no issue on running the app Online. I also tried to debug it on Chrome developer tool but LocalStorage always get 0 data. I used Chrome/Safari Browser and also build the apps as Android using Phonegap build and still not working.
Did I miss something?
Does anyone can provide the details to deal with this Issue.
Below is my code:
Store:
Ext.define('Local.store.News', {
extend:'Ext.data.Store',
config:{
model: 'Local.model.Online',
proxy:
{
type: 'ajax',
extraParams: { //set your parameters here
LookupType: "Phone",
LookupName: ""
},
url: 'MY WCF REST URL',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
reader:
{
type: 'json'
, totalProperty: "total"
},
writer: { //Use to pass your parameters to WCF
encodeRequest: true,
type: 'json'
}
},
autoLoad: true
}
});
Offline Model:
Ext.define('Local.model.Offline', {
extend: 'Ext.data.Model',
config: {
idProperty: "ID", //erm, primary key
fields: [
{ name: "ID", type: "integer" }, //need an id field else model.phantom won't work correctly
{ name: "LookupName", type: "string" },
{ name: "LookupDescription", type: "string" }
],
identifier:'uuid', // IMPORTANT, needed to avoid console warnings!
proxy: {
type: 'localstorage',
id : 'news'
}
}
});
Online Model:
Ext.define('Local.model.Online', {
extend: 'Ext.data.Model',
config: {
idProperty: "ID", //erm, primary key
fields: [
{ name: "ID", type: "integer" }, //need an id field else model.phantom won't work correctly
{ name: "Name", type: "string" },
{ name: "Description", type: "string" }
]
}
});
Controller:
Ext.define('Local.controller.Core', {
extend : 'Ext.app.Controller',
config : {
refs : {
newsList : '#newsList'
}
},
/**
* Sencha Touch always calls this function as part of the bootstrap process
*/
init : function () {
var onlineStore = Ext.getStore('News'),
localStore = Ext.create('Ext.data.Store', { storeid: "LocalNews",
model: "Local.model.Offline"
}),
me = this;
localStore.load();
/*
* When app is online, store all the records to HTML5 local storage.
* This will be used as a fallback if app is offline more
*/
onlineStore.on('refresh', function (store, records) {
// Get rid of old records, so store can be repopulated with latest details
localStore.getProxy().clear();
store.each(function(record) {
var rec = {
name : record.data.name + ' (from localStorage)' // in a real app you would not update a real field like this!
};
localStore.add(rec);
localStore.sync();
});
});
/*
* If app is offline a Proxy exception will be thrown. If that happens then use
* the fallback / local stoage store instead
*/
onlineStore.getProxy().on('exception', function () {
me.getNewsList().setStore(localStore); //rebind the view to the local store
localStore.load(); // This causes the "loading" mask to disappear
Ext.Msg.alert('Notice', 'You are in offline mode', Ext.emptyFn); //alert the user that they are in offline mode
});
}
});
View:
Ext.define('Local.view.Main', {
extend : 'Ext.List',
config : {
id : 'newsList',
store : 'News',
disableSelection : false,
itemTpl : Ext.create('Ext.XTemplate',
'{Name}-{Description}'
),
items : {
docked : 'top',
xtype : 'titlebar',
title : 'Local Storage List'
}
}
});
Thanks and Regards
1) First of all when you creating record and adding into store, the record fields should match the model fields of that store.
Here you creating record with field name, but Local.model.Offline didn't name field
var rec = {
name : record.data.name + ' (from localStorage)'
};
This is what you need to do within refresh
localStore.getProxy().clear();
// Also remove all existing records from store before adding
localStore.removeAll();
store.each(function(record) {
console.log(record);
var rec = {
ID : record.data.ID,
LookupName : record.data.Name + ' (from localStorage)',
LookupDescription : record.data.Description
};
localStore.add(rec);
});
// Don't sync every time you add record, sync when you finished adding records
localStore.sync();
2) If specify idProperty in model which is using localStorage, then record will not be added into localStorage.
Model
Ext.define('Local.model.Offline', {
extend: 'Ext.data.Model',
config: {
// idProperty removed
fields: [
{ name: "ID", type: "integer" }, //need an id field else model.phantom won't work correctly
{ name: "LookupName", type: "string" },
{ name: "LookupDescription", type: "string" }
],
identifier:'uuid', // IMPORTANT, needed to avoid console warnings!
proxy: {
type: 'localstorage',
id : 'news'
}
}
});
In this app, an incident is something that happened, and a feeling is a nested object that describes how you felt about it. Here's my Incident model:
window.Incident = Backbone.RelationalModel.extend({
urlRoot: "/incidents",
idAttribute: "_id",
relations:[{
type: Backbone.HasMany,
key: 'feelings',
relatedModel: 'Feeling',
reverseRelation: {
key: 'incident',
includeInJSON: '_id'
}
},
{
type: Backbone.HasMany,
key: 'thoughts',
relatedModel: 'window.Thought',
reverseRelation: {
key: 'incident',
includeInJSON: '_id'
}
}],
// rest of model...
});
And here is the Feeling model:
window.Feeling = Backbone.RelationalModel.extend({
urlRoot: '/incidents',
idAttribute: '_id'
});
At this point, I can CRUD incidents, and also feelings. But, feelings are not being assigned the reverse relation. In a feeling, the attribute 'incident' is given the value 'null'. In my MongoDB collection, I get these two unrelated objects:
{ "description" : "I feel sad", "_id" : ObjectId("50d3b1462ff17f07cf000002") }
{ "name" : "asdf", "intensityBefore" : "asdf", "intensityAfter" : "asdf", "incident" : null, "_id" : ObjectId("50d3b14e2ff17f07cf000003") }
I have the full project up at https://github.com/mhurwi/cbt-app/tree/relational.
Note, this app is built off a starter app by Christophe Coenraets: https://github.com/ccoenraets/nodecellar
It's been many hours now, and I cannot understand why the relationship is not being set by backbone-relational.
You may need to declare BackboneRelational.HasOne on the Feelings model, like so:
window.Feeling = Backbone.RelationalModel.extend({
urlRoot: '/incidents',
idAttribute: '_id',
relations:[{
type: Backbone.HasOne,
key: 'incident',
relatedModel: 'Incident'
}]
});