Backbone.js: Load multiple collections with one request - javascript

At this time my code fetches each collection separately but given that the same requests are made together I would like it to load the collections in one request.
This is what my request response would look like if I were to merge all:
[{id: 5, milestoneName: 'some milestone', tasks: [{id: 1, taskName: 'first task', dueDate: 'Jan 15'}, {id: 2, taskName: 'second task', dueDate: ''}]},{id: 6, milestoneName: 'some other milestone', tasks: [{id: 3, taskName: 'third task', dueDate: 'Jan 16'}, {id: 4, taskName: 'fourth task', dueDate: ''}]}]
Basically, there are milestones that contain tasks. At this point the milestone collection fetches the milestones and when they are fetched, the task collection(of each milestone) is initialized and fetches the tasks. This can take quite a while (2-3 seconds, that are obvious). If I could load them in one request everything would work faster.
milestoneModel = Backbone.Model.extend({});
milestoneCollection = Backbone.Collection.extend({
url: 'http://some-url',
model: milestoneModel
});
taskModel = Backbone.Model.extend();
taskCollection = Backbone.Collection.extend({
url: 'http://task-url',
model: taskModel
});
I would like the taskCollection to be part of the each milestoneModel and to be reset as soon as this request response arrives.

Ah. Nested models and fetching it all. :-) Basically, have your server send exactly what you're sending. Your JSON structure of the nested tasks collections are fine as is. Here is what you do.
In your Milestone Model, you modify the parse to look for the property tasks then you process it accordingly. For example:
parse: function(response) {
if (_.has(response, 'tasks')) {
if (this.tasks) { // Check if this model has a tasks collection already defined
this.tasks.reset(response.tasks); // It does, so reset it with data
} else {
this.tasks = new taskCollection(response.tasks); // It doesn't, so create one
}
delete response.tasks;
// Don't forget to delete tasks from response or it will be passed onto the model
// set and appear in your attributes
}
return response; // Do pass on all the other attributes that belong to the model
}
This assumes you want the taskCollection as a property of your Milestone model and not an attribute. It basically checks if a tasks array is present as a property of the response, if there is one, we check the model object if it has the tasks collection already defined. If it does, reset the collection with the data. If not, create the collection with data.
One more thing. I'm not sure if you have to do this or not. But I think when you fetch() your Milestones collection, you might have to pass an option parse:true. I kind of forget when you need to do this, or if this a relic from a previous Backbone. Try placing a console.log() in your parse to check if your Milestone model is properly parsing the response. If it isn't, then try passing the parse:true option.

Related

How to refresh data from Meteor.Router subscription?

I've been making a simple blog site using Meteor.js. In the index, the page shows recent posts and the list can be rearranged by their tags and category. It also (for sure) has separate pages for each post.
When the index is refreshed, the list is ordered well, sorted by publishedAt. When I try to arrange posts by tags or category, or even when I go to see an individual post, it's good. But when I go back to the index, the last fetched posts (or a post in the latter case) are always on the top of the list, not properly sorted by publishedAt.
For example, let's say we have the list of 4-3-2-1. By sorting them, I have 3-2. When I go back to the index, now the list goes like 3-2-4-1.
When I console.log() from client.js, those last fetched posts are logged first, and then the other posts including the logged ones are logged. How can I fetch posts without being affected by previous fetched data?
client.js
Posts = new Mongo.Collection("posts");
Router.route('/', {
name: 'index',
template: 'index',
layoutTemplate: 'ApplicationLayout',
waitOn: function() {
return Meteor.subscribe('recentPosts');
},
data: function() {
return {
posts: Posts.find()
};
}
});
Router.route('/category/:_category', {
name: 'category',
template: 'category',
layoutTemplate: 'ApplicationLayout',
waitOn: function() {
return Meteor.subscribe('categorizedPosts', this.params._category);
},
data: function() {
return {
posts: Posts.find(),
category: this.params._category
};
}
});
server.js
Posts = new Mongo.Collection("posts");
Meteor.publish('recentPosts', function() {
return Posts.find({}, {
sort: {publishedAt: -1},
fields: postListFields
});
});
Meteor.publish('categorizedPosts', function(category) {
return Posts.find({
$or: [{category: {$regex: category}}]}, {
sort: {publishedAt: -1},
fields: postListFields
});
});
The order of the locally stored data has no relation to the order of the documents that the server gives you (as you've discovered). Consider also the case where multiple publications with different sort orders are sent to one local collection - how could that be handled?
In your client code's data context you need to specify a sort order to the find() method. This will sort the locally stored posts using MiniMongo. It will not cause a new server request, only the subscribe method does that.
You may be wondering what the point is sorting on the server? As an example, if you're limiting the number of posts that you're sending to the client then it's very important that the server sorts them first so that it sends the right ones (e.g. the first 10 of posts sorted by date) over the DDP link.
A result of this is that you very often need to replicate the sort order for the find() method on both the server and client. You may wish to try to user some common code or variable to ensure that they're in sync.
On your "Posts" collection try with a method submitted: new Date(), then sort with it in your helper : return Posts.find({}, {sort: {submitted: -1}});

Angular.js accessing and displaying nested models efficiently

I'm building a site at the moment where there are many relational links between data. As an example, users can make bookings, which will have booker and bookee, along with an array of messages which can be attached to a booking.
An example json would be...
booking = {
id: 1,
location: 'POST CDE',
desc: "Awesome stackoverflow description."
booker: {
id: 1, fname: 'Lawrence', lname: 'Jones',
},
bookee: {
id: 2, fname: 'Stack', lname: 'Overflow',
},
messages: [
{ id: 1, mssg: 'For illustration only' }
]
}
Now my question is, how would you model this data in your angular app? And, while very much related, how would you pull it from the server?
As I can see it I have a few options.
Pull everything from the server at once
Here I would rely on the server to serialize the nested data and just use the given json object. Downsides are that I don't know what users will be involved when requesting a booking or similar object, so I can't cache them and I'll therefore be pulling a large chunk of data every time I request.
Pull the booking with booker/bookee as user ids
For this I would use promises for my data models, and have the server return an object such as...
booking = {
id: 1,
location: 'POST CDE',
desc: "Awesome stackoverflow description."
booker: 1, bookee: 2,
messages: [1]
}
Which I would then pass to a Booking constructor, which would resolve the relevant (booker,bookee and message) ids into data objects via their respective factories.
The disadvantages here are that many ajax requests are used for a single booking request, though it gives me the ability to cache user/message information.
In summary, is it better practise to rely on a single ajax request to collect all the nested information at once, or rely on various requests to 'flesh out' the initial response after the fact.
I'm using Rails 4 if that helps (maybe Rails would be more suited to a single request?)
I'm going to use a system where I can hopefully have the best of both worlds, by creating a base class for all my resources that will be given a custom resolve function, that will know what fields in that particular class may require resolving. A sample resource function would look like this...
class Booking
# other methods...
resolve: ->
booking = this
User
.query(booking.booker, booking.bookee)
.then (users) ->
[booking.booker, booking.bookee] = users
Where it will pass the value of the booker and bookee fields to the User factory, which will have a constructor like so...
class User
# other methods
constructor: (data) ->
user = this
if not isNaN(id = parseInt data, 10)
User.get(data).then (data) ->
angular.extend user, data
else angular.extend this, data
If I have passed the User constructor a value that cannot be parsed into a number (so this will happily take string ids as well as numerical) then it will use the User factorys get function to retrieve the data from the server (or through a caching system, implementation is obviously inside the get function itself). If however the value is detected to be non-NaN, then I'll assume that the User has already been serialized and just extend this with the value.
So it's invisible in how it caches and is independent of how the server returns the nested objects. Allows for modular ajax requests and avoids having to redownload unnecessary data via its caching system.
Once everything is up and running I'll write some tests to see whether the application would be better served with larger, chunked ajax requests or smaller modular ones like above. Either way this lets you pass all model data through your angular factories, so you can rely on every record having inherited any prototype methods you may want to use.

Backbone.js - make POST request without saving a model

I'm wondering is there a possibility to make this.model.save(data, options) in Backbone without writing data to model?
I have a server endpoint where I need to POST json { field1: 'bbb', field2: 'aaa' } - server writes these fields into db like list: [{ field1: 'bbb', field2: 'aaa' }].
The problem is that Backbone adds these fields to model as well. And I have model like { id: '111', field1: 'bbb', field2: 'aaa', list: [{ field1: 'bbb', field2: 'aaa' }] } and that's wrong..
Is there a way to make save without writing send data to model? Or maybe simply use jquery $.post method and set model on success?
I think the easiest way to fix this would be to either change the data returned by the server, or override the parse method in your model and remove the extra attribute.
A second thought would be to give your moel a single attribute of list and set that with the data dict and have the server return the same. This won't be 100% compatible with general Backbone model usage though.
gotta go..
You can define sync function and make ajax there
You can define parse function and write something like this
parse:function(data)
{
return data.list;
}
2 is the better approach. If setting some field will affect another one, server will return it and you will always have actual data in the model.

Extjs 3.4 custom JSONReader

I haven't had a question from quite a while, but I finally ended up hitting a wall here. Anyways, to make this easy. I am trying to create a JSON store in extjs 3.4 (work related, and no I am not updating).
Well, I created the queries as usual with the proper response and fill it up. My problem is that I have 1 extra property on the JSON that I want to be able to pull and use on the app.
Sample of my JSON from the response object in chrome:
myInventory: [{Priority:1, PMNumber:444, Description:fix-a-tape, Assets:3, Storage_Count:0,…},…]
percent: 97.040498442368
totalCount: "3"
Now, I know this is correctly formatted because the Grid I am using gets populated, but I can't get the percent property. So my question is, how do you pull an extra parameter on the datastore building block of code when you have one extra parameter that is not usual on EXTjs, in my case the percent?
I tried doing a metachange on the JSONReader, but all I get is percent:"percent" on the properties as I inspect the datastore after it's creation.
Ext.data.JsonReader has a property jsonData which is exactly what you need. A quick example:
Ext.onReady(function() {
var store = new Ext.data.JsonStore({
url: 'data/test.json',
root: 'dataRoot',
fields: ['fieldA', 'fieldB'],
idProperty: 'id',
storeId: 'myStore'
});
store.on('load', function(store) {
console.log(store.reader.jsonData.someOtherProperty);
});
store.load();
});
and the data/test.json looks like this:
{
dataRoot: [{
id: 0,
fieldA: 'a',
fieldB: 'b'
}],
someOtherProperty: 'abc'
}
There could also be an alternative approach that you manually (not via store.load()) perform Ext.Ajax request, use the properties you need and then manually call Ext.data.JsonStore.loadData() using the data you got from Ajax request.

fetch is not updating collection.model.models

I have a backbone collection that i've initialized like this:
myCollection = new MyCollection([], {type: 'animals', params: {username: 'steve'}});
myCollection.fetch();
console.log(myCollection) // prints out an object that includes 'models' and the newly fetched models
console.log(myCollection.models) // prints out an empty list []
does anyone know why?
fetch is an asynchronous operation so whatever you do immediately after fetch is most likely executed before the fetch is finished, which leads to quite random results. put the console logging inside the fetch's success-function and see what happens
The model of your collection must have an url to the server to fetch it into a collection, my thought that you have it on "MyCollection", just in case. And then you only need to add a success callback to display the collection populated, like this:
myCollection = new MyCollection([], {type: 'animals', params: {username: 'steve'}});
myCollection.fetch({
success : function(returnedCollection, response){
console.log(returnedCollection);
console.log(returnedCollection.models);
}
});

Categories

Resources