Sending extra, non-model data in a save request with backbone.js? - javascript

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);
});
...

Related

Parse.com Add User Pointer to a class in before save

I have a table called Roster where it stores a User Pointer in one of the column. I am trying set that column in before save method when it is new.So far i have this but i am not sure how to get the user id
Parse.Cloud.beforeSave("Roster",function(request,response){
//Update roster with sender Id
if (request.object.isNew()){
var userPointer = /*NOT SURE HOW TO GET WHO SENT IT*/
request.object.set("User",userPointer);
}
response.success();
});
Any idea?
var userPointer = request.user;
Is the proper method. Todd's answer works on the client side, but not on cloud code, where beforeSave triggers occur.
If you need to access any of the user's information beyond their id, you'll have to first fetch the user, as the entire object is not sent in the request.
Edit - Just wanted to add that before/afterSave triggers have a 3 second timeout. This is enough time to perform a quick query or two, but if you have a lot of objects in your database, or perform many save/fetch/query calls, you may end up exceeding your 3 second limit. If you have a lot of that logic that needs to occur, rather than saving the object from the client, call a cloud code function that handles all of those changes, then saves the object and returns the newly saved object, so that you can set your client side object to the returned, up to date object.
As Jake T pointed out, you will need to use
var user = request.user
Parse.User.current() is not supported in a cloud code environment.

MEAN / AngularJS app check if object already posted

I have thig angularJS frontend and I use express, node and mongo on the backend.
My situation looks like:
//my data to push on server
$scope.things = [{title:"title", other proprieties}, {title:"title", other proprieties}, {title:"title", other proprieties}]
$scope.update = function() {
$scope.things.forEach(function(t) {
Thing.create({
title: t.title,
//other values here
}, function() {
console.log('Thing added');
})
})
};
//where Thing.create its just an $http.post factory
The HTML part looks like:
//html part
<button ng-click="update()">Update Thing</button>
Then on the same page the user has the ability to change the $scope.things and my problem is that when I call update() again all the things are posted twice because literally thats what I'm doing.
Can someone explain me how to check if the 'thing' its already posted to the server just to update the values ($http.put) and if its not posted on server to $http.post.
Or maybe its other way to do this?
I see a few decisions to be made:
1) Should you send the request after the user clicks the "Update" button (like you're currently doing)? Or should you send the request when the user changes the Thing (using ngChange)?
2) If going with the button approach for (1), should you send a request for each Thing (like you're currently doing), or should you first check to see if the Thing has been updated/newly created on the front end.
3) How can you deal with the fact that some Thing's are newly created and others are simply updated? Multiple routes? If so, then how do you know which route to send the request to? Same route? How?
1
To me, the upside of using the "Update" button seems to be that it's clear to the user how it works. By clicking "Update" (and maybe seeing a flash message afterwards), the user knows (and gets visual feedback) that the Thing's have been updated.
The cost to using the "Update" button is that there might be unnecessary requests being made. Network communication is slow, so if you have a lot of Thing's, having a request being made for each Thing could be notably slow.
Ultimately, this seems to be a UX vs. speed decision to me. It depends on the situation and goals, but personally I'd lean towards the "Update" button.
2
The trade-off here seems to be between code simplicity and performance. The simpler solution would just be to make a request for each Thing regardless of whether it has been updated/newly created (for the Thing's that previously existed and haven't changed, no harm will be done - they simply won't get changed).
The more complex but more performant approach would be to keep track of whether or not a Thing has been updated/newly created. You could add a flag called dirty to Thing's to keep track of this.
When a user clicks to create a new Thing, the new Thing would be given a flag of dirty: true.
When you query to get all things from the database, they all should have dirty: false (whether or not you want to store the dirty property on the database or simply append it on the server/front end is up to you).
When a user changes an existing Thing, the dirty property would be set to true.
Then, using the dirty property you could only make requests for the Thing's that are dirty:
$scope.things.forEach(function(thing) {
if (thing.dirty) {
// make request
}
});
The right solution depends on the specifics of your situation, but I tend to err on the side of code simplicity over performance.
3
If you're using Mongoose, the default behavior is to add an _id field to created documents (it's also the default behavior as MongoDB itself as well). So if you haven't overridden this default behavior, and if you aren't explicitly preventing this _id field from being sent back to the client, it should exist for Thing's that have been previously created, thus allow you to distinguish them from newly created Thing's (because newly created Thing's won't have the _id field).
With this, you can conditionally call create or update like so:
$scope.things.forEach(function(thing) {
if (thing._id) {
Thing.update(thing._id, thing);
}
else {
Thing.create(thing);
}
});
Alternatively, you could use a single route that performs "create or update" for you. You can do this by setting { upsert: true } in your update call.
In general, upsert will check to see if a document matches the query criteria... if there's a match, it updates it, if not, it creates it. In your situation, you could probably use upsert in the context of Mongoose's findByIdAndUpdate like so:
Thing.findByIdAndUpdate(id, newThing, { upsert: true }, function(err, doc) {
...
});
See this SO post.
#Adam Zemer neatly addressed concerns I raised in a comment, however I disagree on some points.
Firstly, to answer the question of having an update button or not, you have to ask yourself. Is there any reason why the user would like to discard his changes and not save the work he did. If the answer is no, then it is clear to me that the update should not be place and here is why.
To avoid your user from loosing his work you would need to add confirmations if he attempts to change the page, or close his browser, etc. On the other if everything is continuously saved he has the peace of mind that his work is always saved and you dont have to implement anything to prevent him from loosing his work.
You reduce his workload, one less click for a task may seem insignificant but he might click it many time be sure to have his work save. Also, if its a recurrent tasks it will definitely improve his experience.
Performance wise and code readability wise, you do small requests and do not have to implement any complicated logic to do so. Simple ng-change on inputs.
To make it clear to him that his work is continuously save you can simply say somewhere all your changes are saved and change this to saving changes... when you make a request. For exemple uses, look at office online or google docs.
Then all you would have to do is use the upsert parameter on your mongoDB query to be able to create and update your things with a single request. Here is how your controller would look.
$scope.update = function(changedThing) { // Using the ng-change you send the thing itself in parammeter
var $scope.saving = true; // To display the saving... message
Thing.update({ // This service call your method that update with upsert
title: changedThing.title,
//other values here
}).then( // If you made an http request, I suppose it returns a promise.
function success() {
$scope.saving = false;
console.log('Thing added');
},
function error() {
//handle errors
})
};

Backbone and pusher - what data to send?

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);
}
});

Backbone.js I want to do model.fetch and have the data reflected back in the source collection

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.

How do I run a basic GET / synch request in Backbone?

I'm no sure I'm using the correct words, but I've looked at the localTodos app, and a few other online tutorials.
I'm reading in to Addy's free online book here:
http://addyosmani.github.io/backbone-fundamentals/#implementation-specifics
but right now I'm getting too much theory and just need to do a basic GET from my server and populate my Collection.
Can someone provide a hello World for a GET / synch request. All the mysql tables are set up and so is the code that provides a nice JSON stream of my table, neatly organized.
I shouldn't need to install a PHP framework as I can respond with the JSON stream just fine on my own.
I just need a starting point as I'm guessing it will be a few weeks before the book hits this if it does at all.
I tagged this PHP but I don't think it should matter, as all Backbone will see is a JSON stream.
Ok the basics are.
use "fetch" to get something from server.
use "save" to put or post something from server.
use "destroy" to delete something from server.
To perform fetch you'll need a code like this:
Inside your Model
//Coffescript
url: "pathToYourAPi/"
getAllFromServer:->
#fetch()
//Javascript
url: "pathToYourAPi/",
getAllFromServer: function() {
return this.fetch();
}
This is the simplest way to get data from server. But if you want to get an specific data from server, you maybe should pass an Id or something.
//Coffeescript
url:"/pathToYourAPi/"
setAttributes:->
#set("id": 1)
getItenFromServer:->
#fetch()
// Javascript
setAttributes: function() {
return this.set({"id": 1});
},
getItenFromServer: function() {
return this.fetch();
}
It will request to your api path passing the number 1 as "parameter" to server.
If you want to specify the data that you want to sendo to server in another way, you need pass a Object called data when you're "fetching"
example inside model.
//Coffescript
GetSomeData: ->
#fetch({ data:{ id: 1}})
//Javascript
GetSomeData: function() {
return this.fetch({data: {"id": 1}
});
I have a post about tips using backbone, unfortunately it's only available in portuguese.
try to use google to translate it.
http://www.rcarvalhojs.com/dicas/de/backbone/2014/06/04/5dicas-backbone.html.
Hope it helps.

Categories

Resources