I am trying to create a backbone client side application. I am receiving a list of json objects from the server on startup that will be a list of the possible tables exposed from the server, with their structure. E.g. Customers, Orders, Invoices, Employees
I want to create the models, collections and views dynamically based on the data I receive from the server.
Only when I receive the json on load will I know what the models should be and what the relationships between the models should be.
E.g. Customers structure might be Id, CustomerName, Address, Contact Numbers.
Order Structure might be Id, CustomerId, OrderDate, Amount
etc
By building Models, collections, views, controllers dynamically, I could in theory on startup point at another server who might give me a totally different set of tables e.g. : Movies, Actors etc.. with their structures.
Also, if additional fields are added I don't have to change the client side code again. E.g. Customer table might include a new field called ContactPerson
Please assist me as all the examples I saw on backbone is all based on statically defining the models on the client side up front. So create a model and collections and views for Customers, Orders, Invoices, Employees etc. etc.
Best wishes,
Andy
As already mentioned in the comments, Backbone models are dynamic by nature. So this is perfectly valid for example:
// A example dataset, this could be returned as JSON from the server
var jsonDataA = [
{
name: "Foo",
title: "Bar"
},
{
name: "a",
title: "b"
}
],
// A different example dataset
jsonDataB = [
{
make: "X",
model: "Y"
},
{
make: "Z",
model: "ZZ"
}
],
MyModel = Backbone.Model.extend({
/* Empty Model definition */
}),
MyCollection = Backbone.Collection.extend({
model: MyModel
}),
collection = new MyCollection();
collection.reset(jsonDataA);
console.log(collection.models);
collection.reset(jsonDataB);
console.log(collections.models);
Here I have reused the same Collection and Model definition to store completely different datasets.
One part is the raw data, the other part is its relations. You need to transport the metadata also, which contains the types and their relations. Model attributes will be populated automatically.
From your metadata a simple object can be constructed, where the keys describe one entity, for example:
var entites = {};
entities["Customer"] = Backbone.Model.extend({
/* Model definition based on metadata */
});
var parametersFromServer = {name: "John Doe"};
var customer = new entities["Customer"](parametersFromServer);
For building relations I would recommend using BackboneRelational plugin.
Related
I am using Backand BaaS for my ionic app. My model consists of two tables:
1. Users
2. Trips
A one-to-many relationship has been established between 'Users' and 'Trips'.
'Users' has a collection field that is a collection of 'Trips' and 'Trips' has a owner field that is an object of 'Users'.
What is the correct way to create an object so that upon creating I am assigning the correct owner ('Users' object) to 'Trips' collection field ?
When POSTing a new Trip use this for the Trip Object
{
tripName:"asdf"
owner:1
}
In the users object it will look like this
{
id:1
fName:"david"
trips:[{__metadata: {id: 1}, tripName: "asdf"}]
}
Got a server returning a JSON object like so:
{
'key1':'value'
'key2':{
'key2_0':'value'
}
}
And a collection:
var Collection = Backbone.Collection.extend({
url:api.url//which returns the object above
});
var collection = new Collection();
collection.fetch({
success:function(data){
//do something
}
});
Now i need to use certain properties of the collection throughout my application, but say i need key1, i always have to do collection.at(0).get('key1');//returns 'value', because the data returned is stored within the collection, in a new Array at key 0.
Question:
How to directly... collection.get('key1')//now returns undefined... because it is.
I know i could expose an object to the global scope in the collection success function some_other_var = data.toJSON()[0] and access the some_other_var properties directly, but that's not what i'm looking for;
In order to use the get() function from a Backbone.Collection you need to know the model id or cid wanted.
For instance, lets say your data coming from the server is like follow:
[{
id: '123',
name: 'Alex'
}, {
id: '456',
name: 'Jhon'
}]
In that case you can do this:
this.collection.get('123').get('name') // Return "Alex"
Keep in mind that collection is just a set of model, so behind the scenes by doing collection.get() you are getting a model
Tip: If you don't have any kind of id in your server data, there is always the option of using underscore methods:
find
filter
some
contains
etc
It seems like you're trying to ascribe attributes to a collection, but a collection is merely a set of models. Having additional data that is constant throughout the collection suggests that it should be wrapped inside another Model, which is demonstrated here: Persisting & loading metadata in a backbone.js collection
Here's my situation:
I have a Mongo Database with the following schemas:
var SurveySchema = new Schema({
start_date: Date,
end_date: Date,
title: String,
questions: [ObjectId] //this I want to refer to questions
});
var QuestionSchema = new Schema({
question_text: String,
type: String,
....... (Lots and lots of fields)
});
Full Schema file: https://github.com/nycitt/node-survey-builder-api-server/blob/master/schemas.js
Backbone models: https://github.com/nycitt/node-survey-builder/blob/master/app/js/survey-builder/models.js and collection https://github.com/nycitt/node-survey-builder/blob/master/app/js/survey-builder/collections.js
The Questions should be able to stand alone (be part of 2-3 surveys say). But a survey should refer to the question.
My ideal flow would be:
Backbone calls: GET /api/survey/123 and retreives the survey object where Mongoose translates the Object IDs to actual objects (does the join)
Backbone calls: POST /api/survey/123 posting ONLY the IDs of the questions (is there some Backbone Relational magic to do this)
With ExtJS 3.x, I was able to use the "fields" property of a Store, but it seems with ExtJS 4 I have to absolutely use a Model. It's fine, but in my case, it's not a static Model, and I need to define the fields on the fly and sometimes to change them.
I could re-create a Model, but I need to use a different name as it's apparently not possible to modify an exisiting Model, neither delete it. If I try to use Ext.regModel with the same name, ExtJS crashes.
Thanks for your help!
4.1 UPDATE:
As an update... in 4.1 there is now a static method setFields which can be used to define the model prototype fields. It works well in a controller's init method.
When I did this, I wanted to have some static fields defined in the model class and then set some more dynamically. Unfortunately the new setFields method replaces all fields with the argument, it was easy enough to handle though.
This example uses the MVC pattern where my model and store are included in the controller's model array and store array (providing me with the handy getters used below):
Ext.define('ST.controller.Main', {
extend: 'Ext.app.Controller',
models: ['User', 'Reference'],
stores: ['CurrentUser', 'PermissionRef'],
views: ['MainPanel'],
init: function() {
var me = this;
me.getPermissionRefStore().on('load', function(store, records) {
var model = me.getUserModel();
// this returns the static fields already defined
// in my User model class
fields = model.prototype.fields.getRange();
// add the permission options (dynamic fields) to the static fields
Ext.each(records, function(permission) {
fields.push({name: permission.get('name'), type: 'bool'});
});
// 4.1 method to update the User model fields
model.setFields(fields);
// now load the current user (it will use the updated model)
me.getCurrentUserStore().load();
});
}
});
The User model and CurrentUser store are created exactly like regular, non-dynamic models and stores would be and included in their respective controller arrays, the 'User' model is simply missing the dynamic fields which are added as shown above.
I also went into that problem. I have a service which is responsible for fetching metadata from the server and adapting the models and stores to this metadata.
I therefore defined an empty model and configured the store to use this model.
When the meta data is processed, I add the new/additional fields to the prototype of the model like this (metaDataStore is the store containing the meta data, model is the model which can be obtained from the model manager):
var fields = [];
metaDataStore.each(function(item) {
fields.push(Ext.create('Ext.data.Field', {
name: item.get('field')
}));
});
model.prototype.fields.removeAll();
model.prototype.fields.addAll(fields);
When I then call load on a store using this model or create new model instances the new fields are processed correctly.
Here's a very simple example. Just use a normal Ext.data.Store but instead of a model, specify the fields property:
// you can specify a simple string ('totally')
// or an object with an Ext.data.Field ('dynamic')
var fields = ['totally', {name : 'dynamic', type : 'string'}];
var newStore = new MyApp.store.Object({
fields : fields
// other options like proxy, autoLoad...
});
Don't specify a model property - it seems that it would override the fields property.
I also wanted to change the columns and content of an existing grid dynamically:
// reconfigure the grid to use the new store and other columns
var newColumns = [
{header: 'Totally', dataIndex: 'totally'},
{header: 'Dynamic', dataIndex: 'dynamic'}
];
myGrid.reconfigure(newStore, newColumns);
From the Ext JS 4 documentation about the "fields" property of Ext.data.Store:
This may be used in place of
specifying a model configuration. The
fields should be a set of
Ext.data.Field configuration objects.
The store will automatically create a
Ext.data.Model with these fields. In
general this configuration option
should be avoided, it exists for the
purposes of backwards compatibility.
For anything more complicated, such as
specifying a particular id property or
assocations, a Ext.data.Model should
be defined and specified for the model
config.
So be careful - Sencha may remove it in the future.
I want to implement some kind of tagging functionality to my app. I want to do something like...
class Item(db.Model):
name = db.StringProperty()
tags = db.ListProperty(str)
Suppose I get a search that have 2 or more tags. Eg. "restaurant" and "mexican".
Now, I want to get Items that have ALL, in this case 2, given tags.
How do I do that? Or is there a better way to implement what I want?
I believe you want tags to be stored as 'db.ListProperty(db.Category)' and then query them with something like:
return db.Query(Item)\
.filter('tags = ', expected_tag1)\
.filter('tags = ', expected_tag2)\
.order('name')\
.fetch(256)
(Unfortunately I can't find any good documentation for the db.Category type. So I cannot definitively say this is the right way to go.) Also note, that in order to create a db.Category you need to use:
new_item.tags.append(db.Category(unicode(new_tag_text)))
use db.ListProperty(db.Key) instead,which stores a list of entity's keys.
models:
class Profile(db.Model):
data_list=db.ListProperty(db.Key)
class Data(db.Model):
name=db.StringProperty()
views:
prof=Profile()
data=Data.gql("")#The Data entities you want to fetch
for data in data:
prof.data_list.append(data)
/// Here data_list stores the keys of Data entity
Data.get(prof.data_list) will get all the Data entities whose key are in the data_list attribute