model.save - saves to the server only once - javascript

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.

Related

Passing data between controllers in MVC Javascript

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

Save JavaScript prototype based objects in sessionStorage?

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().

Titanium, Alloy use instance of a model in two windows

I am using Alloy Titanium and I wanted to do something like this :
I have a model, view and controller, this is the view index.xml -
<Alloy>
<Model src="post" instance="true" id="postIns"/>
<Window class="container" onSwipe="update" model="$.postIns">
<Label id="postTitle" top="15">{$.postIns.title}</Label>
<Label id="postContent">{$.postIns.body}</Label>
<Button id="updateButton" onClick="update" bottom="0">Zemi nov post</Button>
</Window>
this is the model - post.js -
exports.definition = {
config: {
"defaults": {
"userId": "",
"id": "",
"title": "Title",
"body": "",
},
adapter: {
type: "properties",
collection_name: "post"
}
},
extendModel: function(Model) {
_.extend(Model.prototype, {
// extended functions and properties go here
});
return Model;
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
// extended functions and properties go here
});
return Collection;
}
};
and my controller index.js that connects to a fake api and fills the instance of the model -
var id = 1;
function update() {
id =_.random(0, 50);
var results = {};
var client = Ti.Network.createHTTPClient({
// called when the response data is available
onload : function(e) {
results = JSON.parse(client.responseText);
// display results on console
Ti.API.info(JSON.stringify(results,null,2));
// save the results to the instance
$.postIns.save(results);
},
// called when an error occurs, including a timeout
onerror : function(e) {
results = JSON.parse(client.responseText);
// display error results on the console
//Ti.API.err(JSON.stringify(results,null,2));
},
});
var url = "http://jsonplaceholder.typicode.com/posts/" + id;
client.open("GET", url);
client.send();
}
$.index.open();
Now let's say I wanted to make another view file .xml with a different window, how would I go about using the same instance of the post model in that window?
P.S. I am pretty sure that the model instance I made is local, but I am interested in a solution about binding a model to more windows.
You can check the titanium docs which clearly explains about the global singleton instance of the model and I think you will be able to use it through out.
Check out the Titanium doc words :
You can also create a global singleton instance of a model, either in markup or in the controller, which may be accessed in all controllers. Use the Alloy.Models.instance method with the name of the model file minus the extension as the only parameter to create or access the singleton.
// This will create a singleton if it has not been previously created,
// or retrieves the singleton if it already exists.
var book = Alloy.Models.instance('book');
Hope it gives some idea.

Using Backbone Fetch Success Callback to Change Data Before Initializing View

I'm looking for a way to intercept the value returned from a server when I fetch a backbone model (a collection, strictly speaking) from the server, then modify it before continuing. I would think that I could do something like this
SessionController.prototype._initPages = function() {
return App.pages.fetch({
reset: true,
success: function(model, response, options) {
//modify the contents of response
}
};
And my modifications would be reflected in the model that's used to initialize the view.
However I was looking at the backbone source and I think I may have misunderstood something.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
var success = options.success;
var collection = this;
options.success = function(resp) {
var method = options.reset ? 'reset' : 'set';
collection[method](resp, options); //this line updates the model
if (success) success(collection, resp, options); // my success callback
collection.trigger('sync', collection, resp, options);
};
wrapError(this, options);
return this.sync('read', this, options);
}
For my needs, it seems the two commented lines need to be switched, though I assume I'm just misunderstanding how to use this feature.
How can I modify the server response before it becomes my model?
I think you could just override the parse function to modify your data as needed
http://backbonejs.org/#Model-parse

Calling Backbone.Model.Save converts child-Backbone.Model to object?

I'm creating a Backbone Model which has a child Backbone Model inside of it:
console.log("inside add item, video:", video instanceof Backbone.Model);
var playlistItem = new PlaylistItem({
video: video,
title: video.get('title')
});
afterwards, I save it:
playlistItem.save({}, {
success: function() {
console.log("Successfully saved.");
playlistItem.get('video').get('title');
}
});
In this example, I encounter an error -- video is not an instanceof Backbone.Model after calling save. Why?
I think you will need to override parse and toJSON.
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
// replace backbone model with json.
json.video = this.get('video').toJSON();
return json;
},
parse: function(data) {
// take json of video and set into model.
this.get('video').set(data.video);
delete data.video;
return data;
},
If you don't parse the json data like this, backbone is going to take the 'video' object from the json and overwrite your Backbone model.

Categories

Resources