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'
}]
});
Related
I'm trying to create a three cascading dropdown lists. First one constains projects, second contains tasks for selected project and the last one sites for selected task.
I want to use the Backbone-Relational plugin, but have hard time to create the proper relations. That's my first time with this plugin.
Code so far:
App.Models.ProjectItem = Backbone.RelationalModel.extend({
default: {
id: 0,
name: ''
},
relations: [{
type: Backbone.HasMany,
key: 'tasks',
relatedModel: App.Models.TaskItem,
//includeInJSON: Backbone.Model.prototype.idAttribute,
collectionType: App.Collections.TasksCollection,
reverseRelation: {
key: 'projectId',
//includeInJSON: Backbone.Model.prototype.idAttribute,
type: Backbone.HasOne
}
}]
});
App.Collections.ProjectsCollection = Backbone.Collection.extend({
model: App.Models.ProjectItem
});
App.Models.TaskItem = Backbone.RelationalModel.extend({
default: {
id: 0,
name: ''
},
relations: [{
type: Backbone.HasMany,
key: 'sites',
relatedModel: App.Models.SiteItem,
includeInJSON: Backbone.Model.prototype.idAttribute,
collectionType: App.Collections.SitesCollection,
reverseRelation: {
key: 'taskId',
//includeInJSON: Backbone.Model.prototype.idAttribute,
type: Backbone.HasOne
}
}]
});
App.Collections.TasksCollection = Backbone.Collection.extend({
model: App.Models.TaskItem
});
App.Models.SiteItem = Backbone.RelationalModel.extend({
default: {
id: 0,
name: ''
}
});
App.Collections.SitesCollection = Backbone.Collection.extend({
model: App.Models.SiteItem
});
Creating single project:
var item = new App.Models.ProjectItem({
id: 1,
name: 'project',
tasks: [
{
id: 1,
name: 'task',
sites: [
{
id: 1,
name: 'site'
}
]
}
]
})
This object serialized to json looks like this:
"{"id":1,"name":"task","tasks":[1],"sites":[{"id":1,"name":"site"}],"projectId":null}"
My questions:
1) Why the sites array is not nested in the tasks one?
2) Sites collection is not serialized the same way that tasks one is. Should I create a relations in the site model too?
3) Why the projectId is returned for the root?
After many hours of trials and errors I finally got it. The order matters..
Working code:
App.Models.SiteItem = Backbone.RelationalModel.extend({
default: {
siteId: 0,
name: ''
}
});
App.Collections.SitesCollection = Backbone.Collection.extend({
model: App.Models.SiteItem
});
App.Models.TaskItem = Backbone.RelationalModel.extend({
default: {
taskId: 0,
name: ''
},
relations: [{
type: Backbone.HasMany,
key: 'sites',
relatedModel: App.Models.SiteItem,
//includeInJSON: Backbone.Model.prototype.idAttribute,
collectionType: App.Collections.SitesCollection,
reverseRelation: {
key: 'task',
includeInJSON: 'taskId',
type: Backbone.HasOne
}
}]
});
App.Collections.TasksCollection = Backbone.Collection.extend({
model: App.Models.TaskItem
});
App.Models.ProjectItem = Backbone.RelationalModel.extend({
default: {
projectId: 0,
name: ''
},
relations: [{
type: Backbone.HasMany,
key: 'tasks',
relatedModel: App.Models.TaskItem,
// includeInJSON: 'taskId',
collectionType: App.Collections.TasksCollection,
reverseRelation: {
key: 'project',
includeInJSON: 'projectId',
type: Backbone.HasOne
}
}]
});
App.Collections.ProjectsCollection = Backbone.Collection.extend({
model: App.Models.ProjectItem
});
For sample data:
var item = new App.Models.ProjectItem({
projectId: 2,
name: 'first',
tasks: [
{
taskId: 1,
name: 'task',
sites:[{siteId:1, name: 'site'}]
}
]
});
I got this json string:
"{"projectId":2,"name":"first","tasks":[{"taskId":1,"name":"task","sites":[{"siteId":1,"name":"site","task":1}],"project":2}]}"
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.
I have a situation where I have a model meant to store several lists of chemicals. The chemical model is the same for each of the hasMany relationships.
I need something like this:
Ext.define('HandSurvey.model.ChemicalRisks', {
extend: 'Ext.data.Model',
requires: ['Ext.data.identifier.Uuid'],
config: {
idProperty: 'id',
identifier: 'uuid',
fields: [
{ name: 'id', type: 'auto' }
],
associations: [
{
type: 'hasMany',
model : 'HandSurvey.model.SpecificChemical',
name : 'fiberglassResins',
store : {
type: 'sql'
}
},
{
type: 'hasMany',
model : 'HandSurvey.model.SpecificChemical',
name : 'paintsStains',
store : {
type: 'sql'
}
},
],
proxy: {
type: 'sql'
}
}
});
But this would cause each list to bind to every SpecificChemical that belongs to the ChemicalRisks model, not just the ones meant to belong to that hasMany. It seems as though I would need to join on multiple fields
Is it possible to do this? Or do I have to make a bunch of the exact same models/stores with different names?
sure you can!
use associationKey and the autogenerated stores of the associations
associations: [
{
type: 'hasMany',
model : 'HandSurvey.model.SpecificChemical',
name : 'fiberglassResins',
associationKey : 'fiberglassResins'
},
{
type: 'hasMany',
model : 'HandSurvey.model.SpecificChemical',
name : 'paintsStains',
associationKey : 'paintsStains'
},
]
Given a response like this: {
"response" : {
"fiberglassResins": [
{
"id" : 1
"name" : "Polyester"
},
{
"id" : 2
"name" : "E-Glass"
}
],
"paintsStains": [
{
"id" : 1
"name" : "item1"
},
{
"id" : 2
"name" : "item2"
}
]
}
}
Then you bind your main model to a store lets say ItemsStore.
IMPORTANT each record of ItemsStore will get autogenerated by Sencha: fiberglassResinsStore and paintsStainsStore.
Yo can console.log() each record to see the actual stores.
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).