I am using Express and Node to build an app.
I have a route called '/new-poll' and '/poll-create'
//The poll-create route will give the user the option of creating a poll
app.route('/poll-create')
.get(function(req, res) {
res.sendFile(path + '/public/pollcreation.html');
});
//This is the route for creating a new poll by authenticated users. Authentication still needs to be added.
app.route('/poll-create')
.post(function(req, res) {
console.log('inside poll-create post request');
console.log(req.body);
serverHandler.newPoll(req, res, db, function(id) {
console.log('It worked');
req.session.poll_id = id;
res.json(id);
});
});
//The above response will redirect to this route, and here is where the poll data will be served up
app.route('/new-poll')
.get(function(req, res) {
console.log('Inside get request for new poll');
console.log(req.session.poll_id);
res.sendFile(path + '/public/pollvisualization.html');
});
//This is the route for making a post request to the same URL. Specifically to obtain the document inserted previously through creating a new poll
app.route('/new-poll')
.post(function(req, res) {
console.log('Inside new poll post');
serverHandler.check(db, req.session.poll_id, function(err, doc) {
if (err) {
console.log('There is an error');
throw err;
}
if (doc) {
res.json(doc); //send the json document generated by the poll creation by mongoDb to pollvisualizationClient.js through ajax-functions.js
}
});
});
Now, I have 2 controllers, controllerData and controllerNonData.
controllerData passes in data to the above POST request using an AJAX call. controllerNonData needs to access the data passed to the POST request by controllerData.
How can I do this in the simplest possible manner? Essentially, my question boils down to what is the easiest way to pass data between view controllers in Express and Node?
The way I'm doing it right now is, I make a POST request with data from controllerData and then make a POST request without data from controllerNonData and then try to differentiate between the two calls in the POST request. But, it seems like a giant pain!
NOTE: I am not using AngularJS in my app. Mentioning this because all the answers I have seen on StackOverflow mention ways to do this in AngularJS.
EDIT:
Code for controllerData
(function() {
$(document).ready(function() {
if (typeof FB !== 'undefined' && FB !== null) { //this if statement is to ensure FB object loads before doing anything else
FB.Event.subscribe('auth.authResponseChange', function() {
FB.getLoginStatus(function(response) {
var data = {}; //setting up the data object to fill with objects
$('.submit-butt').on('click', function() {
data.formData = $('form').serializeArray(); //this is the form data from the form
data.facebookData = response.authResponse; //facebook object data
console.log(data);
data = JSON.stringify(data); //this is done to pass the data and parse it through body parser. Not sure why it works this way.
ajaxFunctions.ready(ajaxFunctions.ajaxRequest('POST', appUrl + '/poll-create', data, function() {
window.open('https://fcc-votingapp-redixhumayun.c9users.io/new-poll', '_self');
}));
return false; //setting this statement to false ensures that the form data does not automatically submit independent of the AJAX call
});
});
});
}
else {
location.reload(); //reloads the page in case the if statement is not satisfied.
}
});
})();
Code for controllerNonData
(function() {
var value; //variable to store the value of the radio option selected
var custom_flag = false; //flag variable to check whether Custom radio button was selected
//This is where the AJAX request is initialized
ajaxFunctions.ready(ajaxFunctions.ajaxRequest('POST', appUrl + '/new-poll', null, function(data) {
//Parsing the data into JSON format below
$(document).ready(function() {
//this is the form data that has been provided via the AJAX request
data = JSON.parse(data);
console.log(data);
var options_count = data[0].options_counter; //this variable stores the options_counter, that is the number of options
var options_array = getSeperatedOptions(data[0].options);
var options_length = Object.keys(options_count).length; //finding out the length of the options_counter object in this line
//updating the header element
$('h1').html(data[0].title);
//Invoking the function that will create all of the options required by the user
createOptions(options_length, options_array);
//This method here checks to see if the user has selected Custom as their option
$('.radio-options').on('click', function() {
var entered_value = getEnteredOption(options_length); //calling this function to check if Custom has been chosen.
if (entered_value == options_length) { //parseInt of entered_value will return a NaN. Use this to check against the number that is returned for parseInt of the other radio buttons
$('.custom-div').show();
custom_flag = true; //set the custom flag to true here because Custom radio button was selected
}
});
$('.btn-danger').on('click', function() {
ajaxFunctions.ready(ajaxFunctions.ajaxRequest('POST', appUrl + '/new-poll/delete-poll', data[0]._id, function(data) {
console.log('This is data: '+data); //data contains the number of documents deleted
}));
});
//Submit button event click handler
$('.submit-butt').on('click', function() {
//if statement decides whether the radio button selected was the Custom radio button
if (custom_flag == true) {
var entered_value = $('.custom-text').val();
value = entered_value; //assigning the local entered_value to a global value variable to use in the next AJAX function
}
//else if statement decides whether a radio option button is checked or not! Fires only if Custom not selected
else if ($('.radio-options').is(':checked')) {
var entered_value = getEnteredOption(options_length); //Function call to get option entered by user. Returns the value of the radio button
value = entered_value; //assigning the local entered_value to a global value variable to use in the next AJAX function
}
//Fire this else statement if no option is selected but Submit button is clicked
else {
window.alert('You need to choose an option before trying to submit');
}
if (value.length > 0) {
var dataToPass = {}; //defining this object to pass data as JSON
dataToPass.value = value;
dataToPass = JSON.stringify(dataToPass); //stringify data to pass it through without error
ajaxFunctions.ready(ajaxFunctions.ajaxRequest('POST', appUrl + '/new-poll/option-entered', dataToPass, function(data) {
//This object contains the returned value from the above AJAX call
data = JSON.parse(data);
var optionsArray = getSeperatedOptions(data.value.options); //Keep the difference between optionsArray and options_array in mind AT ALL TIMES!
//This function is used to convert the options_counter object to an array so that it can be used to render the chart using ChartJS
var options_counterArray = convertOptionsCounterToArray(data.value.options_counter);
//call to function to create chart here
createChart(optionsArray, options_counterArray);
}));
}
else {
window.alert('Hi!');
}
});
});
}));
})();
EDIT: I have also updated my routes to use sessions.
So I am assuming that your Controllers are on the client side. http is a stateless protocol. So in order to pass data between states, you need to implement some kind of caching mechanism. There are a few ways to do this:
Use HTML5 localStorage or sessionStorage API directly, based on the length of time you want to save the data. Make sure you clear the storage once you're done with the data, to prevent hitting the 5MB limit. Using localStorage will allow you to use the data till it is manually cleared. sessionStorage will live as long as the tab is open.
Use some other kind of client side storage like Web SQL
If you want to store data within a page reload cycle (i.e. you will lose data once you reload the page), you can create a Javscript singleton function and inject it into both of your AJAX calls as a dependency, and store your response in that object. This is similar to what happens in AngularJS's Service providers. This is cleaner than the other two approaches, but is of course, shorter lived (unless used on conjunction with one of the above Storage APIs)
Creating singletons in Javascript
var UserStore = (function(){
var _data = [];
function add(item){
_data.push(item);
}
function get(id){
return _data.find((d) => {
return d.id === id;
});
}
return {
add: add,
get: get
};
}());
Related
I'm having an issue with the inherent asynchronous-ness of AJAX with what I'm trying to accomplish...
Using a file upload form, I'm trying to send a set of data to my LAMP server which then loops through each subset of the data and generates a PDF for each subset. This takes a good 2 minutes or so to process all of the PDFs since there are so many (like 100) being generated for each subset of data.
I am hoping to use JavaScript/jQuery to segment the data into subsets that I will post to the server one at a time, and as each PDF is generated, get a response from the server and increment a progress bar, then send the next request.
How would you accomplish this in a loop? What's below doesn't work because jQuery doesn't care about the state of the server and sends all requests asynchronously.
var i = 0;
$.each(data_stuff, function(key, value) {
$.post( window.location.href, {
stuff: key,
data: value
}).done(function( data ) {
if (data == 1) {
$('div#counter').text(i);
}
});
$i++;
});
Thanks!
Assuming data_stuff is an Object, you can try this:
var i = 0;
// We need an array (not object) to loop trough all the posts.
// This saves all keys that are in the object:
var keys = Object.keys(data_stuff);
function postNext() {
// For every POST request, we want to get the key and the value:
var key = keys[i];
var value = data_stuff[key];
$.post( window.location.href, {
stuff: key,
data: value
}).done(function( data ) {
if (data == 1) {
$('div#counter').text(i);
}
// Increment i for the next post
i++;
// If it's the last post, run postsDone
if(i == keys.length) {
postsDone();
}
// Else, run the function again:
else {
postNext();
}
});
});
postNext();
function postsDone() {
// All data was posted
}
This will only post the next bit of data_stuff after the previous $.post was done.
Here am trying to make a RESTful call to an external API.
am trying to achieve 2 things in one call. So, I have one function with 2 nested functions within.
The first one calls the search API to search for a product.
The second one calls recommended API to retrieve recommendations based on the results from the first one.
My AngularJS Code is as follow;
var walmartAssn= angular.module('myApp', ['ngResource']);
walmartAssn.controller('walmartAssnController', function($scope,$resource) {
//define the API urls
var urlSearchProductApi= 'http://api.walmartlabs.com/v1/search';
var urlRecProductApi='http://api.walmartlabs.com/v1/nbp';
//define API key
var keyApi='exampleKey123';
$scope.searchProductMethod= function(){
//pass the value from the user input text box
$scope.searchItem = $scope.item ;
$scope.productId;
//get the data from the Walmart product search API
searchRequest = $resource(urlSearchProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" }});
//pass the input text as a parameter through a GET request
$scope.searchedProducts = searchRequest.get({ apiKey: keyApi,
query: $scope.searchItem });
console.log($scope.searchedProducts.$promise);
$scope.searchedProducts.$promise.then(function(eventDetail){
//fetch the ID of the first item
$scope.productId = eventDetail.items[0].itemId;
});
recommendRequest = $resource(urlRecProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" , isArray:true}});
console.log(recommendRequest);
$scope.recommendedProducts = recommendRequest.get({ apiKey:
keyApi, itemId: 42608121 });
console.log($scope.recommendedProducts)
$scope.recommendedProducts.$promise.then(function(){
$scope.recommendedProductsList = eventDetail;
console.log("Print recommended list");
console.log(eventDetail);
console.log($scope.recommendedProductsList);
console.log('End');
});
} });
In the above app, the first function returns result while the second function does not.
In chrome console am getting the following, not the fist function returns an array of JSONs while the second one was blocked.
While on the Network tab in the chrome console, i see the the call was successful, as in shown below;
Moreover, I have tried the URL with hard coded values in the browser and worked successfully.
Any help is appreciated, thanks in advance.
Assuming that the 2nd call does not depend on the first, I see that you are not defining eventDetail as an argument to the second method.
So, instead of:
$scope.recommendedProducts.$promise.then(function(){
It would be:
$scope.recommendedProducts.$promise.then(function(eventDetail){
If you actually mean to use the eventDetail from the first method (the one used with $scope.searchedProducts.$promise), then the whole second request code needs to be called from the first then handler, passing the data needed.
Something like:
var walmartAssn= angular.module('myApp', ['ngResource']);
walmartAssn.controller('walmartAssnController', function($scope,$resource) {
//define the API urls
var urlSearchProductApi= 'http://api.walmartlabs.com/v1/search';
var urlRecProductApi='http://api.walmartlabs.com/v1/nbp';
//define API key
var keyApi='exampleKey123';
$scope.recommend = function(itemId) {
var recommendRequest = $resource(urlRecProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" , isArray:true}});
console.log(recommendRequest);
$scope.recommendedProducts = recommendRequest.get({ apiKey:
keyApi, itemId: itemId });
console.log($scope.recommendedProducts);
$scope.recommendedProducts.$promise.then(function(eventDetail){
$scope.recommendedProductsList = eventDetail.items; // or just `eventDetail`?
console.log("Print recommended list");
console.log(eventDetail);
console.log($scope.recommendedProductsList);
console.log('End');
});
};
$scope.searchProductMethod= function(){
//pass the value from the user input text box
$scope.searchItem = $scope.item ;
$scope.productId;
//get the data from the Walmart product search API
var searchRequest = $resource(urlSearchProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" }});
//pass the input text as a parameter through a GET request
$scope.searchedProducts = searchRequest.get({ apiKey: keyApi,
query: $scope.searchItem });
console.log($scope.searchedProducts.$promise);
$scope.searchedProducts.$promise.then(function(eventDetail){
//fetch the ID of the first item
$scope.productId = eventDetail.items[0].itemId;
$scope.recommend($scope.productId);
});
};
});
One more thing:
Why is isArray:true used only in recommendation but not in search?
Update
It might be worth trying a jQuery JSONP call to see if it works. Maybe the recommendation endpoint does not support JSONP. AngularJS returns 404 in this case according to https://stackoverflow.com/a/24893912/146656
var obj = {
conn : null,
first : function(thisIdentity) {
"use strict";
var myObj = this;
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
success : function(data) {
myObj.conn = new Connection(data.user_id, "127.0.0.1:80");
sessionStorage.setItem('connection', JSON.stringify(myObj.conn));
}
});
},
second : function(thisIdentity) {
"use strict";
var myObj = this;
var conntn = sessionStorage.getItem('connection');
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
success : function(data) {
var parsedConnection = JSON.parse(conntn);
parsedConnection.sendMsg(data.id, data.nid);
}
});
}
};
var Connection = (function() {
function Connection(uid, url) {
this.uid = uid;
this.open = false;
this.socket = new WebSocket("ws://"+url);
this.setupConnectionEvents();
},
Connection.prototype = {
sendMsg : function(id, nid) {
alert("Working");
},
// other functions
}
})();
So connection is made in the AJAX callback function of first and I store the object in the sessionStorage via JSON but when I use it in the AJAX callback of second then error is coming that
TypeError: parsedConnection.sendMsg is not a function
Now I understand that may be it is because JSON can be used to store plain objects not prototype-based objects.
My question is : Can any one tell me how to store prototype-based objects via JSON or any other way to implement this?
I don't want to use eval. Any code, reference would be much appreciated. Thanks!
UPDATE
I did as #Dan Prince mentioned but then a new problem occurred that now when in sendMsg function I use
this.socket.send(JSON.stringify({
action: 'message',
rec: receiver,
msg: message
}));
Then it stays
InvalidStateError: An attempt was made to use an object that is not,
or is no longer, usable
Any inputs? Thanks!
You could probably hack your own solution into place by storing the prototype as a property of the object, then reinstantiating it with Object.create after you read it, but the real question is why do you want to do this in the first place?
I would suggest writing a serialize method on Connection's prototype, which exposes only the essential information (there's no sense serializing a web socket for example).
Connection.prototype.toJSON = function() {
return JSON.stringify({
uid: this.uid,
url: this.url,
open: this.open
});
};
Then use this method when you save the connection object into session storage.
myObj.conn = new Connection(data.user_id, "127.0.0.1:80");
sessionStorage.setItem('connection', myObj.conn.toJSON());
Each saved connection now has the minimum amount of data you need to call the constructor and recreate the instance.
When you load a connection from session storage, parse it and pass the values back into the constructor.
var json = sessionStorage.getItem('connection');
var data = JSON.parse(json);
var connection = new Connection(data.uid, data.url)
// ...
connection.sendMsg(data.id, data.nid);
This will recreate the correct prototype chain in a natural and predictable way.
It's hard to see exactly what you are trying to achieve in every respect, but let's assume :
for various DOM elements, a click handler (delegated to document) will cause asynchronously derived data to be sent via socket.send().
the socket is to be initialized with an asynchronously derived uri.
the socket is to be kept available for immediate reuse.
data by which the socket is initialized is to be cached in local storage for future sessions. (It makes no sense to store the socket itself).
In addition, we need to acknowledge that a socket consume resources should really be disposed of if its resuse is not immediate.
The whole strategy is abnormally complex. The overhead of performing an ajax operation once per session to obtain a uri would typically be accepted, as would the creation of a socket each time one is needed. However, it's an intersting exercise to write something with all the stated characteristics.
This may not be 100% correct but could possibly give you some ideas, including the use of promises to cater for several asynchronisms. Here goes ...
var obj = {
conn: null,
init: function(thisIdentity) {
// It makes sense to attach the click handler only *once*, so let's assume this is an init function.
"use strict";
var myObj = this;
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
}).then(function(data) {
myObj.send(JSON.stringify({
'id': data.id,
'nid': data.nid
}));
});
});
},
send: function(data) {
"use strict";
var myObj = this;
return myObj.getSocket().then(function(socket) {
socket.send(data);
}).then(function() {
// by disposing in later event turn, a rapid series of send()s has at least a chance of using the same socket instance before it is closed.
if(socket.bufferedAmount == 0) { // if the socket's send buffer is empty, then dispose of it.
socket.close();
myObj.conn = null;
}
});
},
getSocket: function() {
"use strict";
var myObj = this;
//1. Test whether or not myObj.conn already exists ....
if(!myObj.conn) {
//2 .... if not, try to recreate from data stored in local storage ...
var connectionData = sessionStorage.getItem('connectionData');
if(connectionData) {
myObj.conn = myObj.makeSocket(connectionData.user_id);
} else {
//3. ... if connectionData is null, perform ajax.
myObj.conn = $.ajax({
url: some value,
// other parameters
}).then(function(data) {
sessionStorage.setItem('connectionData', JSON.stringify(data));
return myObj.makeSocket(data.user_id);
});
}
}
return myObj.conn; // note: myObj.conn is a *promise* of a socket, not a socket.
},
makeSocket: function(uid) {
"use strict";
var myObj = this;
var uri = "127.0.0.1:80"; // if this is invariant, it can be hard-coded here.
// return a *promise* of a socket, that will be resolved when the socket's readystate becomes OPEN.
return $.Deferred(function(dfrd) {
var socket = new WebSocket("ws://" + uri);
socket.uid = uid;
socket.onopen = function() {
myObj.setupConnectionEvents();// not too sure about this as we don't know what it does.
dfrd.resolve(socket);
};
}).promise();
}
};
Under this scheme, the click handler or anything else can call obj.send() without needing to worry about the state of the socket. obj.send() will create a socket if necessary.
If you were to drop the requirement for storing data between sessions, then .send() and .getSocket() would simplify to the extent that you would probably choose to roll what remains of .getSocket() into .send().
I have pass a collection of objects through http post in angular js.
The code is as follows:
$scope.selectedContent = function () {
var contents = $filter('filter')($scope.data.ContentId, { Selected: true }); // I could able to get all the selected objects here, No problem with it
var jsonData = angular.toJson(contents); //It is not able to convert to Json if there are more than 5 records
var promise = $http.post('/webapi/cmsApi/CmsPublishApprovedContent?jsonData=' + jsonData, {});
promise.success(function () {
window.location.reload();
});
[ReferrerFilterAttribute]
[HttpPost]
[System.Web.Http.ActionName("CmsPublishApprovedContent")]
public void CmsPublishApprovedContent(string jsonData)
{
var contents = JsonConvert.DeserializeObject<List<ContentNodeInWorkFlow>>(jsonData);
foreach (var content in contents)
{
_contentService.PublishContent(content.ContentId, userId);
}
}
}
The above code works fine if there are 5 records or less. If there are more records, I could able to get all the selected record
objects in the variable 'contents'. But the problem is occuring when converting to Json for all those objects. I
have about 500 records to pass through. How can do I it?
There is no specific reason to convert to JSON data. I just need to extract the ids of all the selected items. I have modified the above code as below:
$scope.selectedContent = function () {
var contents = $filter('filter')($scope.data, { Selected: true });
var abc = [];
angular.forEach(contents, function(content)
{
abc.push(content.ContentId); // got all the ids in the array now
});
var promise = $http.post('/webapi/cmsApi/CmsPublishApprovedContent' ,{contents : abc});
promise.success(function () {
window.location.reload();
});
}
I have just took an array and pushed all the content ids into it. I could able to see all the ids in the array now. I tried to pass the array as above.
How to retrieve those array in the code behind.
[ReferrerFilterAttribute]
[HttpPost]
[System.Web.Http.ActionName("CmsPublishApprovedContent")]
public void CmsPublishApprovedContent(int[] abc)
{}
I do not see any values obtained under int[] abc. What will be the datatype for the parameter in the method call above.
You need second argument of $http.post method. You have to send such data by POST requests, not in query of url. You can put some data into body of the post request.
You need this:
var postBodyWithHugeAmountOFData = {data: [1,2,3,4,5...500]};
$http.post(url, postBodyWithHugeAmountOFData).success(function () {});
Also, you must be ready to handle this request in your backend.
is there any specific reason u want to pass this data as a JSON?.
if u r using Web API in that case u can pass the object as it is but only make sure that collection in web API method contains all the property in javascript collection
Thank you for all your posts. It's working fine without converting to Json. The code is as below.
$scope.selectedContent = function () {
var contents = $filter('filter')($scope.data, { Selected: true });
var promise = $http.post('/webapi/cmsApi/CmsPublishApprovedContent' ,contents);
promise.success(function () {
window.location.reload();
});
}
and the signature would be
public void CmsPublishApprovedContent(List<ContentNodeInWorkFlow> abc)
{
}
I am running this code in backbone which saves some data to the server,
GroupModalHeaderView.prototype.save = function(e) {
var $collection, $this;
if (e) {
e.preventDefault();
}
$this = this;
if (this.$("#group-name").val() !== "") {
$collection = this.collection;
if (this.model.isNew()) {
console.log("MODEL IS NEW");
this.collection.add(this.model);
}
return this.model.save({ name: this.$("#group-name").val()}, {
async: false,
wait: true,
success: function() {
//console.log($this, "THIS");
console.log('Successfully saved!');
this.contentView = new app.GroupModalContentView({
model: $this.model,
collection: $this.collection,
parent: this
});
this.contentView.render();
return $this.cancel();
},
});
}
};
This works fine the first time I run it, however if I run it again straight after saving my first piece of data it does not save new data it merely updates the last saved data. So the first time I save it runs a POST request and the next time it runs a PUT request, why would this be?
I am not sure if you need this but here is my initialise function -
GroupModalHeaderView.prototype.initialize = function() {
_.bindAll(this)
}
Your view has a model object attached to it. As I understand you fill some forms, put their data to model and save the model. But all the time you have single model object, and you only update it's data. If you want to create a new object after saving model just add a line:
this.model = new YourModelClass();
right after line console.log('Successfully saved!');
From the backbone documentation:
If the model isNew, the save will be a "create" (HTTP POST), if the
model already exists on the server, the save will be an "update" (HTTP
PUT).
If you want to make a post request even if the model is new, just override the default save implementation and call Backbone.sync(method, model, [options]) with 'create' as the passed method.