The server responds with something on the form:
{'dates':
{'2013.05-17':
{'activities':
{'activity 1':
{time: 0, 'synced': false},
'activity 2':
{time: 5, 'synced': false},
'activity 3':...
},...
},
'2013.05-18':
{ ...}, ...},
'id': id}
I currently put everything in a single Backbone model, which doesn't seem like the proper way to do it. The examples I've read around the web all use very simple models where there's no nestled structures and the mapping is pretty simple e.g. {x: 1, y: 2} being mapped to a coordinate model and so on.
What's the "correct" way to map the above JSON structure to Backbones models/collections?
As Protostome mentions, Backbone Relational is good for this kind of thing.
However looking at the JSON data that you have used as an example, from my viewpoint you have only one model and collection as follows:
Activity Model
Activities Collection
Even though you have a nested set of data you could look at it in a different way which gives you a simple backbone model, for example:
var activity = {
id: "Activity 1"
time: 0,
synced: false,
date: "2013.05-17"
batchId: id // this corresponds to the id property in your example
}
This represent all of the data contained in your complex nested hierarchy more simply, and more importantly suited to the way Backbone works.
If you cannot alter what the server provides you, you could use the Underscore library functions (from memory _.map()) to map the JSON you receive into simple JSON objects ready for use with Backbone.
I am hoping that maybe you can simplify your design by thinking outside the the context of sticking with a hierarchy?
Related
Is there a way to configure a JsonRestStore to work with an existing web service that returns an array of objects which is not at the root-level of the JSON response?
My JSON response is currently similar to this:
{
message: "",
success: true,
data: [
{ name: "Bugs Bunny", id: 1 },
{ name: "Daffy Duck", id: 2 }
],
total: 2
}
I need to tell the JsonRestStore that it will find the rows under "data", but I can't see a way to do this from looking at the documentation. Schema seems like a possibility but I can't make sense of it through the docs (or what I find in Google).
My web services return data in a format expected by stores in Ext JS, but I can't refactor years worth of web services now (dealing with pagination via HTTP headers instead of query string values will probably be fun, too, but that's a problem for another day).
Thanks.
While it's only barely called out in the API docs, there is an internal method in dojox/data/JsonRestStore named _processResults that happens to be easily overridable for this purpose. It receives the data returned by the service and the original Deferred from the request, and is expected to return an object containing items and totalCount.
Based on your data above, something like this ought to work:
var CustomRestStore = declare(JsonRestStore, {
_processResults: function (results) {
return {
items: results.data,
totalCount: results.total
};
}
});
The idea with dojo/store is that reference stores are provided, but they are intended to be customized to match whatever data format you want. For example, https://github.com/sitepen/dojo-smore has a few additional stores (e.g. one that handles Csv data). These stores provide good examples for how to handle data that is offered under a different structure.
There's also the new dstore project, http://dstorejs.io/ , which is going to eventually replace dojo/store in Dojo 2, but works today against Dojo 1.x. This might be easier for creating a custom store to match your data structure.
Note: I imagine that this will be a super easy question for anyone with Ember experience. Don't be daunted by the length of my question. Skip down to the bottom if you don't want to read all of the overhead.
Overhead
My company has a project coming up which requires the use of front-end technologies to accomplish what I would otherwise prefer to do with PHP.
We looked into a few different JavaScript frameworks, and the solution that we agreed upon was Ember.js.
I followed the TodoMVC tutorial on their website, and learned the very basics.
With this project, we will be using an AJAX request to pull in our data at the start of the application, and then put everything into fixtures.
I'm having difficulty figuring out how to pass multiple fixtures into my template at the same time. I started with adding two fixtures. Here are their data and definitions:
App.Students = DS.Model.extend({
first: DS.attr('string'),
last: DS.attr('string'),
classes: DS.hasMany('ClassGroup')
});
App.ClassGroup = DS.Model.extend({
className: DS.attr('string'),
isActive: DS.attr('number'),
students: DS.hasMany('Students',{ async: true })
});
App.ClassGroup.FIXTURES = [
{
id: 1,
className: 'Class 1',
isActive: 1,
students: [1, 2]
},
{
id: 2,
className: 'Class 2',
isActive: 0,
students: [2]
}
];
App.Students.FIXTURES = [
{
id: 1,
first: 'Student',
last: 'One',
classes: [1]
},
{
id: 2,
first: 'Student',
last: 'Two',
classes: [1, 2]
}
];
This is just a very simple implementation. The actual data will have dozens of relations, but I've simplified things for the purpose of learning this framework.
Here is how I am currently setting up my router:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('ClassGroup');
}
});
Doing it this way allows me to access the data via Handlebars by using {{#each model}} and then something like {{className}} to access it's data members. I am also able to jump to the students array by nesting another each statement like {{#each students}}.
However, I cannot figure out how to get more than one entry point into my data. For example, by passing ClassGroup via the router, I am unable to access the Students fixture by itself; I must first loop through ClassGroup, and from there, access a student.
Likewise, if I change my router to pass in the students fixture, I can loop through the students, and then jump to the classes via a nested each statement, but I lose the ability to simply loop through a list of all classes.
Is there a way that I can pass all of my fixtures into the template? Am I going about this the right way?
The Long Story Short
How can I pass ALL of my fixtures into the template at once, in such a way that I can access my students array or my classes array? How can I access said fixture data (i.e., if I want to display the first name of the student with ID 2, represented as students[2]['first'] in a language like PHP, how can this be done with handlebars)?
That's right, the Template only has access to what it's been passed by the Controller. In this case, since you don't explicitly set up the controller, and the model is an array, it'll be an array controller, hence you ability to do {{#each}} to iterate over the ClassGroups (you actually don't even need model). You haven't passed in the students array anywhere explicitly, nor created it in the controller, so you don't have access to it in the template. Fortunately, Ember has a setupController route hook which does exactly this kind of thing. In your example:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('ClassGroup');
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('students', this.store.find('Students'));
}
});
Now you'll have a students property available on your controller and therefore your template.
I'm new to Backbone and to MongoDB. I'm running into problems destroying models (that are backed by MongoDB). I believe it has something to do with the fact that a MongoDB "document" uses the abnormal _id attribute and is not wired up with Backbone by default. I've tried setting idAttribute: '_id' which does not seem to solve the issue.
A sample Mongo document looks like:
{
_id: Object
$oid: "527303e82f3504ba5bf4b21f"
__proto__: Object
feeling: "ok"
location: "california"
name: "Kevin"
}
models.coffee
class Models.Campaign extends Backbone.Model
urlRoot: "http://localhost:4567/api/campaigns"
# setting idAttribute: '_id' causes issues rendering the Backbone collection
controller.coffee
...
model.destroy() #=> only removes the item from the dom but does not make a DELETE request
Am I able to pass in an id to the destroy() function?
Does anyone have advise in how to properly wire up Backbone with MongoDB without having to monkeypatch a ton of built-in Backbone functions?
I think you'll have an easier time if you add a parse to your model to sort out the confusing data you're getting from the server. You don't need or care about _id, you do care about $oid, and Backbone would prefer to work with an id property; you can sort out all three with a simple parse in your model like this:
parse: function(response) {
var parsed = _(response).pick(
'feeling',
'location',
'name'
);
parsed.id = response.$oid;
return parsed;
}
Demo: http://jsfiddle.net/ambiguous/pn773/
You'd probably be fine if your parse modified response rather than making a semi-copy using _.pick and modifying that copy, however, parse doesn't technically own response so it would be rude to change it and there could be surprising side effects. Good habits, etc.
Then when Backbone tries to convert the server's data to a model, it will end up working with:
{
id: "527303e82f3504ba5bf4b21f",
feeling: "ok",
location: "california",
name: "Kevin"
}
and everyone should be happy:
Backbone won't see the irrelevant _id.
Backbone won't see $oid and you won't need to set idAttribute to anything special.
Backbone will see an id like it expects and all the standard machinery will use the BSON ObjectId as the model's unique identifier.
The simple method should get you your DELETE /api/campaigns/527303e82f3504ba5bf4b21f request when you model.destroy().
In the context of a web app, I have a server which sends or receives JSON strings based on the input from the client. On client consumption, these JSON strings are immediately converted into JavaScript objects where they will live out their lives as objects. These objects are not arrays; they represent complex, arbitrary data models, each property of which can have an arbitrary number of unique subproperties or objects.
var myView = {
name: 'root'
id: 'root_0'
children: {
child_1: {
arbitraryid: 'root_0_child_1',
type: 'Department',
name: 'Produce',
quadrant: 1,
children: {
child_1: {
arbitraryid: 'root_0_child_1_child_1',
type: 'Aisle',
number: 3,
length: 12,
children: { }
}
}
},
child_2: {
arbitraryid: 'root_0_child_2',
name: 'West',
type: 'Region',
children: {
child_1: {
arbitraryid: 'root_0_child_2_child_1',
name: 'Wegmans',
type: 'Store',
children: {
child_1: {
arbitraryid: 'root_0_child_2_child_1_child_1',
type: 'Department',
children: { }
}
}
}
}
}
}
};
When I build the JSON string server side, I guarantee that all objects will have 'children' and 'arbitraryid' properties; but everything else is dynamically generated and the properties and values are completely arbitrary.
If this were XML, I could use jQuery to var someChild = myView.find('#root_0_child_1_child_1'). This would get me a jQuery object with the results of the find AND not only a reference to myView but a position from which to move omnidirectionally through the object: var someChild = myView.find('#root_0_child_1_child_1').parent().
Does a utility exist to solve this problem for native, JavaScript objects or is there a preferable way/methodology to do this? I'd like to avoid writing a bunch of this type of code to simply get at my property and then potentially loop again to update the parent object.
while (obj.hasOwnProperty('children')) {
for (var child in obj) {
//..etc, etc
}
}
Most of the SO questions I see on this subject deal with searching arrays, frequently with predictable data table style construction.
Mapping is possible, but these objects quickly become deep and that option seems little better than dumb looping.
Ideas?
Edit: rolling my own utility class.
I'm still exploring other libraries/utilities, but I wrote a generic helper class to do searches:
ObjectHelper
While useful, I think it illustrates some of the difficulty with getting at other jQuery-like functionality. Not only would I like to search, but I'd like to be able to crawl up/down the object property structure similarly to the way you can chain .parent().children().find() operators together. Doable, but complicated.
I found a few JSON Query Languages:
JSONPath
JAQL
LINQ to Javascript
JSINQ
jLinq
SQLike (despite the name, it's used for querying JSON)
TrimQuery (uses a SQL-like language to query JSON objects)
This page sums them up and goes through their pros and cons.
There is a DOJO module for querying JSON as well:
JSONQuery
I'm not sure how stable/standard these are though.
I solved this by rolling my own classes. ShadesJS is pretty basic right now, but it has some methods for crawling JavaScript objects and working with Web Storage. On the to-do is implement parent/child methods to get some JQuery-esque flexibility. It's not hard to do, but it's tricky to get it right and performant.
I have a situation using backbone.js where I have a collection of models, and some additional information about the models. For example, imagine that I'm returning a list of amounts: they have a quantity associated with each model. Assume now that the unit for each of the amounts is always the same: say quarts. Then the json object I get back from my service might be something like:
{
dataPoints: [
{quantity: 5 },
{quantity: 10 },
...
],
unit : quarts
}
Now backbone collections have no real mechanism for natively associating this meta-data with the collection, but it was suggested to me in this question: Setting attributes on a collection - backbone js that I can extend the collection with a .meta(property, [value]) style function - which is a great solution. However, naturally it follows that we'd like to be able to cleanly retrieve this data from a json response like the one we have above.
Backbone.js gives us the parse(response) function, which allows us to specify where to extract the collection's list of models from in combination with the url attribute. There is no way that I'm aware of, however, to make a more intelligent function without overloading fetch() which would remove the partial functionality that is already available.
My question is this: is there a better option than overloading fetch() (and trying it to call it's superclass implementation) to achieve what I want to achieve?
Thanks
Personally, I would wrap the Collection inside another Model, and then override parse, like so:
var DataPointsCollection = Backbone.Collection.extend({ /* etc etc */ });
var CollectionContainer = Backbone.Model.extend({
defaults: {
dataPoints: new DataPointsCollection(),
unit: "quarts"
},
parse: function(obj) {
// update the inner collection
this.get("dataPoints").refresh(obj.dataPoints);
// this mightn't be necessary
delete obj.dataPoints;
return obj;
}
});
The Collection.refresh() call updates the model with new values. Passing in a custom meta value to the Collection as previously suggested might stop you from being able to bind to those meta values.
This meta data does not belong on the collection. It belongs in the name or some other descriptor of the code. Your code should declaratively know that the collection it has is only full of quartz elements. It already does since the url points to quartz elements.
var quartzCollection = new FooCollection();
quartzCollection.url = quartzurl;
quartzCollection.fetch();
If you really need to get this data why don't you just call
_.uniq(quartzCollecion.pluck("unit"))[0];