I am working a on backbone application at the moment, that "talks" to an API. A user can edit an organisation for example, the PATCH request will go to the API and get saved to the database, on a successful the API then "talks" to Pusher via this line,
Pusherer::trigger('organisation_'.$id, 'organisation:change', json_encode(array('organisation' => $organisation)));
Basically this telling pusher to trigger an event on the organisation_21 channel, the event that has happended is the organisation:change one, and the data to send is the organisation model.
What happens then on the Backbone side is that that i bind a method on to that channel and when the event happens that method will run, and update the view for the subscribed user.
HOWEVER, the data for my organisations has gotten quite big, the JSON object is 11.8kb, pusher won't process anything more than 10kb, is there a better way to work with backbone, my api and pusher other than sending the entire model?
On suggestion I like the idea, of doing the save, and fetching the model for new data in realtime via pusher. Would that look something like this?
organisationChanged:function(){
var self = this;
this.model.get('organisations').fetch({ //send GET To /api/organisation/id
success: function(model, response) {
self.model.get('organisations').set(response);
}
});
}
Fetch the model and set the attributes returned from the server to those of the model - so far this sounds correct to me yes? The complication comes that the model also contains a couple of collections, will set work on these, or is there a better way?
The edit is the right idea, but with a little changes.
The fetch would automatically change the model. From your code, it looks like your model is a bigger model, and organization is just a sub model. If this is the case:
var organizationmodel=this.model.get('organisations') // This would get the organization model.
organizationmodel.fetch({ //send GET To /api/organisation/id
success: function(model, response) {
// the model, and the organizationmodel both actually should point to the same object
// and they are already changed based on the server returned stuff. so no need to do a set.
// if they are not, you can just set it again.
self.model.set('organizations',model);
}
});
Related
I am still a little confused about the way Ember fetches data from remote API and save them in the browser.
So I have created a REST Adapter to get sets of records with an Ajax call, a serializer, and the corresponding model. Suppose they are posts, and I have successfully made a posts index page where the user can click on any post to get into the post detail page.
The Ajax call happens on the index page, and using the Ember inspector, it is clear that all the records are stored in the local store.
But when I click the "back link" which is available on every post detail page, it does redirect to '/posts/' but it seems to make the ajax call again. So all the posts are fetched from the API once again making the page much less responsive.
Here's my questions:
How does that part of Ember work and how do I make Ember simply get the records from the local store without making Ajax call again and again? (unless the user refresh the browser)
If I make a GET request to 'post/1' , no data will be available since in this route no Ajax call should be made. But how do I let the data show? Should I set up another REST adapter for individual post or is it possible to get the record from the local store if an Ajax call has been made?
Hope that makes sense and thanks in advance!
Update:
My post adapter:
App.PostAdapter = DS.RESTAdapter.extend({
findAll: function(store, type, sinceToken) {
var url = 'THE URL TO GET JSON BACK';
return $.getJSON(url).then(function(data) {
return posts;
})
}
});
My Post and Posts routes:
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.postId);
}
})
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
})
Regarding your first question: It depends on the model callback of your route. If you use the all method of the store, the Ajax Request won't be made again. (But: You'd be responsible to get the data the first time around. You way want to sideload it somewhere or may want to call find if all didn't return anything. It depends on your application.
Regarding your second question: The RESTAdapter should be available for single data items as well as for lists. You can implement a model hook in the route using find. If you link-to this route with an object (instead of an ID), this hook won't be called. So the hook would only be called when needed.
I'm really looking to understand the concept here (as opposed to receiving a code solution).
I have a collection that was populated by a fetch. I want to take a single model from the collection, fetch the latest data from the server, then have the data that came from the server put back into the collection's version of the model.
Restating with terms from Backbones docs: I'd like to ensure that I have the latest server state. I won't care about "change" events that are triggered because I'm expecting the model to change almost every time. Fetch resets the model's state from the server, so I should just be able to do something like this, right?:
[pseudo]
model.fetch();
in the fetch success handler call one of these:
this: collection.add(model, {merge: true});
..or: collection.set(model);
If that's not enough to accomplish my task, what concept am I missing?
EDIT - adding details
I call fetch():
controller.model.fetch({
success: function(model){
console.log(model);
updateView();
},
error: function(){
console.error('error fetching contact model');
}
});
The success callback is fired after the fetch(). I can see that the model has new data and that the Image has been modified and needs to be re-downloaded:
When I call my function that updates the view, nothing changes visibly because the latest data is still in the "changed" member...it seems. What's the standard way to get the latest data from "changed" into the "attributes" member? And is this made clear in the documentation?
There is no "collection's version of the model"
If you refer to a model in the collection and call fetch, there's nothing else needs to be done.
This model is already a part of the collection and the new data will populate.
Objects in javascript are passed by reference, so unless you clone it seeing something like model.clone(), you are referring to the same object.
I am using ember.js for UI side development of application. For every action we are showing a modal popup and we have forms in that pop ups and then on form submission we have some respective actions which have controllers. Its working fine upto here, if we are making a web service call it is taking the server response and updating respective model js file in store.
So new requirement came that in that form if we give some value in a particular text field then a server call should go and fetch some information. Now the problem is, this value is not updating in store. The html(template) is like any html form and mouseout has some action calling on a particular field. That action is
App.MainFormSubmitActionController = App.ModalController.extend({
needs : 'application',
actions : {
actionToBeCalled : function() {
this.store.find('xyzModel');
//the above line should normally find xyzModal in store if not found then hit server and then update the store too
},
mainFormSubmitAction : function() {
//some task done here
}
}
});
the json object I am getting back from server is :
{"payload":{"xyzModel":{"gmp":25.0,"type":"someType","id":1}},"status":"SUCCESS"}
and the js file is
App.XyzModel = DS.Model.extend({
"type" : DS.attr(),
"gmp" : DS.attr()
});
The server call is happening fine, my problem is why ember store is not getting updated when I am getting a response and model not found error is not there. I google a lot but no one seems to have faced the same problem. What cud have I possibly done wrong.
Ember doesn't expect the model data to be wrapped in anything, what you can do is either change the api behaviour or you can customise your Application or Model serializer like so to extract the model and make it the top level object:
App.XyzModelSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id) {
delete payload.payload.status;
payload = {xyzModel: payload.payload.xyzModel };
return this._super(store, type, payload, id);
}
});
there are other methods for when mutiple records are returned, see http://emberjs.com/api/data/classes/DS.RESTSerializer.html
I don't know the best way to handle huge mongo databases with meteorjs.
In my example I have a database collection with addresses in it with the geo location. (the whole code snippets are just examples)
Example:
{
address : 'Some Street',
geoData : [lat, long]
}
Now I have a form where the user can enter an address to get the geo-data. Very simple. But the problem is, that the collection with the geo data has millions of documents in it.
In Meteor you have to publish a collection on Server side and to subscribe on Client and Server side. So my code is like this:
// Client / Server
Geodata = new Meteor.collection('geodata');
// Server side
Meteor.publish('geodata', function(){
return Geodata.find();
});
// Client / Server
Meteor.subscribe('geodata');
Now a person has filled the form - after this I get the data. After this I search for the right document to return. My method is this:
// Server / Client
Meteor.methods({
getGeoData : function (address) {
return Geodata.find({address : address});
}
});
The result is the right one. And this is still working. But my question is now:
Which is the best way to handle this example with a huge database like in my example ? The problem is that Meteor saves the whole collection in the users cache when I subscribed it. Is there a way to subscribe to just the results I need and when the user reused the form then I can overwrite the subscribe? Or is there another good way to save the performance with huge databases and the way I use it in my example?
Any ideas?
Yes, you can do something like this:
// client
Deps.autorun(function () {
// will re subscribe every the 'center' session changes
Meteor.subscribe("locations", Session.get('center'));
});
// server
Meteor.publish('locations', function (centerPoint) {
// sanitize the input
check(centerPoint, { lat: Number, lng: Number });
// return a limited number of documents, relevant to our app
return Locations.find({ $near: centerPoint, $maxDistance: 500 }, { limit: 50 });
});
Your clients would ask only for some subset of the data at the time. i.e. you don't need the entire collection most of the time, usually you need some specific subset. And you can ask server to keep you up to date only to that particular subset. Bare in mind that more different "publish requests" your clients make, more work there is for your server to do, but that's how it is usually done (here is the simplified version).
Notice how we subscribe in a Deps.autorun block which will resubscribe depending on the center Session variable (which is reactive). So your client can just check out a different subset of data by changing this variable.
When it doesn't make sense to ship your entire collection to the client, you can use methods to retrieve data from the server.
In your case, you can call the getGeoData function when the form is filled out and then display the results after the method returns. Try taking the following steps:
Clearly divide your client and server code into their respective client and server directories if you haven't already.
Remove the geodata subscription on the server (only clients can activate subscriptions).
Remove the geodata publication on the server (assuming this isn't needed anymore).
Define the getGeoData method only on the server. It should return an object, not a cursor so use findOne instead of find.
In your form's submit event, do something like:
Meteor.call('getGeoData', address, function(err, geoData){Session.set('geoDataResult', geoData)});
You can then display the geoDataResult data in your template.
I'm looking for a solution for dealing with an issue of state between models using backbone.js.
I have a time tracking app where a user can start/stops jobs and it will record the time the job was worked on. I have a job model which holds the job's data and whether it is currently 'on'.
Only 1 job can be worked on at a time. So if a user starts a job the currently running job must be stopped. I'm wondering what the best solution to do this is. I mean I could simply toggle each job's 'on' parameter accordingly and then call save on each but that results in 2 requests to the server each with a complete representation of each job.
Ideally it would be great if I could piggyback additional data in the save request similarly to how it's possible to send extra data in a fetch request. I only need to send the id of the currently running job and since this really is unrelated to the model it needs to be sent alongside the model, not part of it.
Is there a good way to do this? I guess I could find a way to maintain a reference to the current job server side if need be :\
when you call a save function, the first parameter is an object of the data that's going to be saved. Instead of just calling model.save(), create an object that has the model data and your extra stuff.
inside of your method that fires off the save:
...
var data = this.model.toJSON();
data.extras = { myParam : someData };
this.model.save(data, {success: function( model, response ) {
console.log('hooray it saved: ', model, response);
});
...