MEAN / AngularJS app check if object already posted - javascript

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

Related

Mongo&Express - what are the practical ways to avoid client firing same req multiple times?

Introduction:
I am developing a liking system similar to normal facebook liking where each user can like each post only once. I need to develop this system and have a "totalLike" in mongoDB. Environment: Mongoose, Express on server-side, Nextjs(React) on client-side.
Problem:
When user spams clicking the LIKE button before the button re-render to UNLIKE, the express LIKE api could be fired multiple times causing the extra number to be put into "totalLike". For example, you clicked like 3 times very fast, there you get 3 totalLikes instead of 1. (noted: I choose to add totalLike to user to avoid too many nested populate when call in the future)
My Pseudo-code:
Simply +1 totalLikes when press like and -1 totalLikes when press unlike. And it should be just that !
router.patch(
'/like/:post',
(req, res, next) => {
// user_id = the_user_who_click_like_to:post
Post.updateOne(
// at this post, add the user_id LikedUser array, then ...
(res) => {
User.updateOne(
// at user_id, do { $inc: { totalLikes: 1 } }, <-- this is the problem
// because "totalLikes" could be plus more than once!
)})})
router.delete(
'/like/:post',
// Do the same but opposite, $pull user_id from Post and -1 totalLikes in User = face same problem as above
)
Now the questions:
What are the practical ways to avoid this from happening on
server-side?
Is blocking the client-side from firing normally done by only setting state to disabling React button ?
As a backend newbie, another improved approach for the shown Psuedo code would be eye candy.
There are of course different approaches you can use.
Usually people to like posts have to be logged in and thus have an account.
This enables you to store in the DB, attached to the user, the like.
When you get a request to like something you can just check if the user has liked that already or not and throw an error in case it's present.
Another way, of course not 100% safe (the first would be), is to use the browser's Memory Storage or send back a cookie.
You would again check if there is this cookie or not before processing the request.
Of course you would then add front-end code to show the like button as inactive if the user has liked it before or popup a login window if he is not logged in.
It's very hard to show a full functional code as it's a pretty big topic, but I hope these directives may help.

Firebase concurrency issue: how to prevent 2 users from getting the same Game Key?

DATABASE:
SITUATION:
My website sells keys for a game.
A key is a randomly generated string of 20 characters whose uniqueness is guaranteed (not created by me).
When someone buys a key, NTWKeysLeft is read to find it's first element. That element is then copied, deleted from NTWKeysLeft and pasted to NTWUsedKeys.
Said key is then displayed on the buyer's screen.
PROBLEM:
How can I prevent the following problem :
1) 2 users buy the game at the exact same time.
2) They both get the same key read from NTWKeysLeft (first element in list)
3) And thus both get the same key
I know about Firebase Transactions already. I am looking for a pseudo-code/code answer that will point me in the right direction.
CURRENT CODE:
Would something like this work ? Can I put a transaction inside another transaction ?
var keyRef = admin.database().ref("NTWKeysLeft");
keyRef.limitToFirst(1).transaction(function (keySnapshot) {
keySnapshot.forEach(function(childKeySnapshot) {
// Key is read here:
var key = childKeySnapshot.val();
// How can I prevent two concurrent read requests from reading the same key ? Using a transaction to change a boolean could only happen after the read happens since I first need to read in order to know which key boolean to change.
var selectedKeyRef = admin.database().ref("NTWKeysLeft/"+key);
var usedKeyRef = admin.database().ref("NTWUsedKeys/"+key);
var keysLeftRef = admin.database().ref("keysLeft");
selectedKeyRef.remove();
usedKeyRef.set(true);
keysLeftRef.transaction(function (keysLeft) {
if (!keysLeft) {
keysLeft = 0;
}
keysLeft = keysLeft - 1;
return keysLeft;
});
res.render("bought", {key:key});
});
});
Just to be clear: keyRef.limitToFirst(1).transaction(function (keySnapshot) { does not work, but I would like to accomplish something to that effect.
Most depends on how you generate the keys, since that determines how likely collisions are. I recommend reading about Firebase's push IDs to get an idea how unique those are and compare that to your keys. If you can't statistically guarantee uniqueness of your keys or if statistical uniqueness isn't good enough, you'll have to use transactions to prevent conflicting updates.
The OP has changed the question a bit so, i will update the answer as follows: I will leave the bottom part about transactions as it was and will put the new update on top.
I can see two ways to proceed:
1) handle the lock system on your own and use JavaScript callbacks or other mechanisms for preventing simultaneous access to a portion of the code.
or
2) Use transactions/fireBase. On this case, i don't have the setup ready to share code other than sample/pseudo code provided at the bottom of this page.
With respect to option 1 above:
I have coded a use-case and put in on plunker. It uses JavaScript callbacks to queue users as they try to access the part of the code under lock.
I. user comes in and he is placed in queue
II. It then calls the callback function which pops users as
first come first out bases. I have the keys on top of the page to
be shared by the functions.
I have a button click event to this and when you click the button twice quickly, you will see keys assigned and they're different keys.
To read this code, click on the script.js file on the left and read starting from the bottom of the page where it calls the functions.
Here is the sample code in plunker. After clicking it, click on Run on top of the page and then click on the button on right hand side. Alert will pop up to show which key is given (note, there are two calls back to back to show two users coming in at same time)
https://plnkr.co/edit/GVFfvqQrlLeMaKlo5FCj?p=info
The fireBase transactions:
Use fireBase transactions to prevent concurrent read/write issues - below is the transaction() method signiture
transaction(dataToBeWritten, onComplete, applyLocally) returns fireBase.promise containing {
committed: boolean, nullable fireBase.database.snapshot }
Note, transaction needs writeOperation as first parameter and in your case looks like you’re removing a key upon success! hence the following function to be called in place of write
Try this pseudo code :
//first, get reference to your db
var selectedKeyRef = admin.database().ref("NTWKeysLeft/"+key);
// needed by transaction as first parameter
function writeOperation() {
selectedKeyRef.remove();
}
selectedKeyRef.transaction(function(writeOperation) , function(error,
committed, snapshot) {
  if (error) {
    console.log('Transaction failed abnormally!', error);
  } else if (!committed) {
    console.log('We aborted the transaction (because xyz).’);
  } else {
    console.log(‘keyRemoved!’);
  }
  console.log(“showKey: ", snapshot.val());
}); // end of the transaction() method call
Docs + to see parameters/return objects of the transaction() method see:
https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
In the Docs.... If another client writes to the location before your new value is successfully written, your update function is called again with the new current value, and the write is retried.
https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions
I don't think the problem you're worried about can happen. JavaScript, including Node, is single-threaded and can only do one thing at a time. If you had a big server infrastructure with more than one server running this code, then it would be possible, but for a single Node program, there's no problem.
Since none of the previous answers discussing the scope of Transactions worked out, I would suggest a different workaround.
Is it possible to trigger the unique code generation when someone buys a code? If yes, you could generate the unique string if the "buy" button is clicked, display the ID and save the ID to your database.
Later the user enters the key in your game, which checks if the ID is written in your database. This might probably also save a bit of data, since you do not need to keep track of the unique IDs before they get bought and you will also not run out of IDs, since they will always get generated when necessary.

Redux - Remove object from store on http response error

Consider the following flow:
I have a page with a list of "products" and a modal to create a single "product". I open the modal, fill the form and submit the form.
At this point, I dispatch an action CREATING_PRODUCT, add the product to the store and send the http request to the server.
I close the modal and display the list of results with the new product.
Let's suppose I receive an error response from the server.
Desired behavior:
I would like to display an error, remove the project from the list, re-open the modal and display the form already filled.
Question
How can I find that project and remove it the list? I don't have an id (or a combination of unique properties) to find that project in the store. I don't see a clean way to link a request/response to that "product" object in the store.
Possible solution
The client adds a "requestId" into the project before adding it to the store. On response error, I dispatch a generic "CREATE_ERROR" and I remove the project with that requestId from the store.
Extra
Same problem with edit and delete. For example during a delete should I keep a reference to the deleted project with the requestId in the store, until the http request is successful?
I bet it is a problem with a common solution, but I can't find examples.
Thanks!
In general, your Redux store should be modeled somewhat like a relational database, in that every time you have a list of data models, each element of the list should have its own identifier. This helps a lot when dealing with more complex data schemes.
You should probably store your projects as an object, something like:
{
// ...other store properties
projects: {
"_0": { // ... project properties }
"_1": { // ... project properties }
// ...more projects...
},
}
This way, whenever you need to mess with an existing project, you can just reference its id and use projects[id] to access that project. This would also solve the edit and delete cases, as you could just pass the IDs around as handles.
I like this short piece on why your Redux store should be mostly flat and why data should always have identifiers very much. It also talks about using selectors to "hide" your IDs away, which may or may not be useful for you.
In your case, as you are getting IDs from a server, you could have an ID prefix which indicates unsaved values. So your projects object would become something like:
projects: {
"_0": { // ... }
"_1": { // ... }
"UNSAVED_2": { // ... }
}
This way, you could easily identify unsaved values and handle them when an error occurs, still get the benefits of generating temp IDs on the client-side in order to revert changes on error, and also warn your user if they try to leave your app while their data still hasn't been synchronized - just check if there are any "UNSAVED" IDs :)
When you get a response from the server, you could change the "UNSAVED_suffix" ID to an actual ID.

How to make a simple database query in Meteor

Every time I have a new idea for an application, I start with Meteor. And every time I end up using something else. And I think it comes down to me not knowing how to do a simple database query.
Here's a hypothetical example. I want to make an app where the user types something into a box, presses a button, and then an image of whatever they typed in shows up. It uses some image search api.
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
<img src="{{img}}"></img>
</template>
Seems simple enough so far. Now there isn't a way to send information to the client without putting it in a database first, as is my understanding. Let's assume we have some function addToDB that takes queries and enters the image information into the database.
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
}
});
Great! That's still not too bad. But now to send the data back to the client...
//server.js
Meteor.publish("image", function(query) {
Images.find({q: query});
}
But wait. We can't just subscribe when the client loads, because we don't know the query yet. So maybe the event handler needs to be
Template.image.events({
'click .showImage': function() {
addToDB($('.description').val());
Deps.autorun(function() {
Meteor.subscribe("images", $('.description').val());
});
}
});
Okay, let's feed that into the template...
Template.image.img = function() {
return Images.findOne().imgsrc;
}
Nope, that results in an error because when the template is first loaded, we haven't subscribed to Images yet. So we can update the template like so:
<template name="image">
<input type="text" class="description"></input>
<button class="showImage"></button>
{{#each info}}
<img src="{{info.img}}"></img>
{{/each}}
</template>
And then change the template filling function to:
Template.image.info = function() {
return Images.find({}, {limit: 1});
}
And voila!
I spent longer than I'm willing to admit stumbling through all of that this evening. If it was just plain old node, I could've used some simple jQuery.
But there are a million amazing features that Meteor has that I really want to take advantage of. So what I'm hoping is that someone can show me the way. What mistakes did I make in this process, and in the final result? What's the nicest, cleanest, simplest way to get this done within a meteor app.
This is so complicated, within a framework that makes so many other things so simple. So how can I just make a simple database query?
Consider looking at the meteor examples they provide. All of the examples have the functionality of taking user input, managing collections, and displaying collection queries to templates.
Most of your code looks fine but you are asking several questions at once and starting from a perspective that makes it difficult to answer. Some general notes:
consider using Session to hold variables a user sets with an event.
subscribe to the data you want by passing Session variable to a Deps.autorun function
display the data you want by passing a Session variable to the template.
basic javascript rules still apply - null.someThingIWant is an error. A handy pattern is return something && something.someThingIWant;
One problem in the code above is that your publish is not returning the query results. I assume that is a typo.

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

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

Categories

Resources