Backbone model.save() fails unless evaluated in DevTools console first - javascript

I have this function in a Backbone view:
updateToServer: function(e) {
e.preventDefault();
var id = e.target.getAttribute('data-id');
var file = this.collection.get(id);
var data = {};
$(e.target).serializeArray().map(function(x) {data[x.name] = x.value;});
file.save(data);
this.$el.modal('hide');
}
If I allow this to run naturally, I get undefined is not a function on file.save(data). However, if I set a breakpoint in Chrome DevTools at file.save(data) and evaluate that function manually in the console before resuming, both save functions work.
Why is this happening and how can I fix it?
Here's the entire view in case you need additional context: https://gist.github.com/raddevon/d3ddf1bba101b6b67c4b#file-supportfilesview-js-L155-L163
Update: New discovery: On the second run, this works. I have an even listener on form submit. When I click the submit button the first time, I get the error. If I click again, the model saves.

Can you try this
updateToServer: function(e) {
e.preventDefault();
var id = e.target.getAttribute('data-id');
var file = this.collection.get(id);
var data = {};
$(e.target).serializeArray().map(function(x) {data[x.name] = x.value;});
this.$el.modal('hide');
setTimeout(function(){
file.save(data);
}, 200); //try with different values for timer
}
I have added a 200 millisecond timer.
This might not be your actual solution but at least you will come to know if there is some asynchronous stuff going on before 'file' is actually formed.
Try different values for the timer. I mean keep increasing the timer and see if you are still not able to get rid of the error.
Once you are sure that 'file' is formed asynchronously then you can look into why that's happening.
And try console.logs instead of debuggers for debugging so that you can test without pausing the execution.
Hope that helps.

This was not at all what I suspected, and I hadn't given enough information in the question without realizing it. The line in my code that triggered the exception was file.save(), but the actual exception was happening inside Backgrid.
I provide a form to allow users to update models from the collection displayed in a grid. A particular column is defined as an integer column, but I hadn't converted the value coming from the form to an integer. As a result, Backgrid was trying to run toFixed on a string. I modified my form serialization code to convert strings containing only integers into integers. Now, everything works as expected.
Here's that serialization code:
$(e.target).serializeArray().map(function(x) {
data[x.name] = x.value === 'on' ? true : x.value;
if (!isNaN(parseInt(data[x.name])) && isFinite(data[x.name])) {
data[x.name] = parseInt(data[x.name]);
}
});
If I had to guess, I'd say that's probably a bit naive, but it seems to be working well in my application.
Thanks to everyone for the help!

Related

ASP ADO recordset.NextRecordset() breaks when there is no next recordset

I'm writing a script in ASP with Javascript, and I need to loop through an indeterminate number of records AND recordsets. I have no problem looping through the individual records. The problem arises when I need to move through recordsets. As long as there is another recordset it works fine. If there are no more recordsets the code dies, and there seems to be no way to check for or catch the error. I've done try/catch blocks and every test I can think of, but the code just breaks and gives internal server error. I was hoping there was an isNextRecordset() or isLastRecordset() function I was missing.
My Current Code
do{
//Some Code Stuff
record = record.NextRecordset(); //Dies here when no more recordsets
}while(!!record);
With normal VBScript they show this method working(never tried it though). But I don't want to use VBScript.
VBScript Code
Do Until record = Nothing
set record = record.NextRecordset
Loop
EDIT:
I believe I have made things confusing by adding the loop, If you remove the loop and just call nextrecordset once when there is no next recordset it still dies. No test for null needed. My code doesn't make it to the test for null.
//Get A recordset
//There is only one recordset
record = cmd.Execute();
//Do some stuff with the record set
//Call NextRecordset
//even though there are no more
record = record.NextRecordset(); //<--- Still Dies right here
EDIT:
Just wanted to point out the hidden problem I was having. The answer below is correct but wasn't my actual problem. Some of the tutorials I saw show a structure like below. Close the record and connection. But by the time the record got the bottom it was already null, which was a problem. This is easily solved with correct error messages showing, which I didn't have. My own stupidity, but might help someone.
try{
//Do Some Stuff
record = record.NextRecordset();
//Test Record set
}catch(e){
}finally{
record.Close(); <---- This is where it was actually having a problem.
record = null;
myconn.Close();
myconn = null;
}
Proper syntax would be:
while (record) {
//Some Code Stuff
record = record.NextRecordset();
}
This way the loop would stop when NextRecordset() will return null.

select2 - get current search text

I'm using select2 and fetching available options from the server with this query function
var recipientsTimeout;
this.loadRecipients = function (query) {
clearTimeout(recipientsTimeout);
recipientsTimeout = setTimeout(function(){
data.transmitRequest('lookupcontacts', { search: query.term }, function(resp){
query.callback({ results: resp.items });
});
}, 500);
};
It uses our own ajax layer, and delays searching until the user stops typing, and it works fine. The only small issue is that if the user types some text, then immediately backspaces over it, my last timeout will still fire, and an ajax request will be fired, and ignored. This doesn't cause any problems, but it's less than optimal.
Is there any (preferably non-brittle) way to fetch whatever the current text is? It seems as though the query object sent in has an element property, which is a jQuery wrapper of the original hidden input I set select2 up with, but there's no direct way I can see to get the actual textbox that the user is typing in. Obviously I could inspect it and easily figure out the dom pattern and build up a selector from the hidden element back to what the user is typing in, but I really hate doing that, since the specific layout could easily change in a future version.
So what is the best way to get the currently entered text, so I could do a quick check on it when the setTimeout expires, and I'm about to run my ajax request.
I'm using 4.0.3 and the way I did it is this:
$("#mySelect2").data("select2").dropdown.$search.val()
The hidden input element (where you initialize select2 on) gets a data property select2, which contains references to all elements, that are used by select2. So you could do something like this:
var hiddenInputSelector = '#e1',
select2 = $(hiddenInputSelector).data('select2'),
searchInput = select2.search;
if($(searchInput).val() === '')
clearTimeout(recipientsTimeout);
This is not in the docs though, so it might change in the future.
In select2 version 4.0.3 this works for me, whereas the others did not:
$('.select2-search__field')[0].value;
I do something like this to get the current search term:
list.data("select2").search[0].value;

Appending data to an element works but then I can't retrieve it

I'm attaching response data to the target of a jquery plugin when a preload option is set. It all seems to work a charm except the data I set is inaccessible. When I log $(elm).data(); I can see that the object was returned with the appropriate data set to the appropriate key. But when I try console.log($(elm).data('key')); I get undefined. I also get undefined when I try var elmData = $(elm).data(); console.dir(elmData.key);. So I'm logging the object on one line, seeing it in the console, trying to access a property I just confirmed exists and getting undefined.
Here is my function:
this.preloadData = function(folders)
{
var getString;
for(var folder in folders ){
getString = 'folder='+folders[folder]+'&uri='+uri+'&thumbSide='+options.thumbSide;
$.get(options.handler, getString,
function(response, serverStat, xhr)
{
$self.data(folder, response);
});
}//for
var $selfData = $self.data();
console.log($selfData);//Object{ editorial : "data", testingdata : 'some meaningless words'}
console.log($self.data());//Object{ editorial : "data", testingdata : 'some meaningless words'}
console.log($self.data.editorial);//undefined
console.dir($self.data('editorial'));//undefined
console.log($selfData['editorial']);//undefined
console.log($selfData.editorial);//undefined
$self.data('testingdata', 'some meaningless words');
console.log($self.data());//Object{ editorial : "data", testingdata : 'some meaningless words'}
console.log($self.data('testingdata'));//'some meaningless words'
}
I know the namespacing is too simple, I just tried to cut out as many factors as I could to try and understand why this isn't working. I'm developing in chrome but I've tried it in firefox also and get the same.
-----------------------------EDIT------------------------------------------
I understand what the problem is now. The console reflects all changes to an object that get made regardless of if they were made when the object was logged. So when I logged the object the property didn't exist yet because the response had yet to come back from the server, but the log still shows the property as existing because at some later point in the script execution it DID exist. Because primitive values aren't logged in the same way, the call to log the property shows up as undefined because it WAS undefined. If I refer to the property some seconds later, say on a click event it is defined(which is how I intended it to work anyway). I just got really caught up in debugging this function before I actually implemented it.
Can you do this?
$self.data['editorial']
try
console.log($self.data.editorial);

Resolving Javascript/jQuery memory leak issues in $.post

I have an issue with a memory leak I'm trying to trace in a webapp, using jQuery-1.7.1. The app is making a POST request to the server to retrieve some search results using an elaborate form. After I eliminated most of the js code (for leak trace purposes), the bare function looks like this:
$(function() {
// bind the search action
$('#search-button').unbind('click').click(function() { doSearch(); });
});
function doSearch() {
// get the query string from the search form
var query = $('#search-form').serialize();
// perform search and render results
$.post('/search', query, function(data){
// nothing here now (trying to debug)
data = null;
}, 'json');
query = null;
}
I can see that the memory consumption accumulates an extra ~1MB every time I hit the search button, even though, in effect, it does nothing. This is a real issue since the app has an "auto-refresh" search mode, where that call is made about once a minute - so if left active it'll jam the browser after a while.
The data object returned from the server contains a boolean for success/failure, and an html string to render (pretty big if successful, around 1Mb or so):
data = {
success : true/false,
html : "<div id='results'>.....</div>"
}
Since I'm down to performing zilch in the actual body of the callback, I suspect that somehow this data is not being eliminated from the scope and is aggregated in memory. I've tried setting it to null at the end of the callback, but that didn't do the trick. This was tested on both chrome and firefox (earlier and latest releases for both). Am I missing something? Any thoughts will help - thanks.
There really is no reliable way to force javascript to do a garbage collection.
You can try adding this line along with the your other line intended to free up memory:
query = null;
delete query;
JSON format needs to have quotes for each key
Try
data = {
"success" : true/false,
"html" : "<div id='results'>.....</div>"
}
I am talking about the serverside string output

JavaScript list saving question

I have this fiddle: http://jsfiddle.net/y8Uju/5/
I am trying to save the numbers, because, when I submit, the list of numbers gets erased. I am a little new to JavaScript so am not quite familiar to what is available. In PHP I would use sessions to save the list, but what can I do in JavaScript to do this?
Here is the JavaScript code:
function bindName() {
var inputNames = document.getElementById("names").getElementsByTagName("input");
for (i = 0; i < inputNames.length; i++) {
inputNames[i].onkeydown = function() {
if (this.value == "") {
setTimeout(deletename(this), 1000);
}
}
}
}
document.getElementById("addName").onclick = function() {
var num1 = document.getElementById("name");
var myRegEx = /^[0-9]{10}$/;
var itemsToTest = num1.value;
if (myRegEx.test(itemsToTest)) {
var form1 = document.getElementById("names");
var nameOfnames = form1.getElementsByClassName("inputNames").length;
var newGuy1 = document.createElement("input");
newGuy1.setAttribute("class", "inputNames");
newGuy1.setAttribute("id", nameOfnames);
newGuy1.setAttribute("type", "text");
newGuy1.setAttribute("value", num1.value);
form1.appendChild(newGuy1);
num1.value = "";
bindName();
}
else {
alert('error');
}
};
function deletename(name) {
if (name.value == "") {
document.getElementById("names").removeChild(name);
}
}
You can use localStorage: http://jsfiddle.net/y8Uju/8/
Loading:
var saved = JSON.parse(localStorage["numbers"] || "[]");
for(var i = 0; i < saved.length; i++) {
document.getElementById("name").value = saved[i];
add(false);
}
Saving:
var saved = JSON.parse(localStorage["numbers"] || "[]");
saved.push(num1.value);
localStorage["numbers"] = JSON.stringify(saved);
And define the function of the addName button separately, so that you can call it when loading as well.
Edit: You have to execute a function when the page is loading to fetch the stored numbers, and add some code to save the entered number when one clicks the Add button.
For storing you can use localStorage, but this only accepts Strings. To convert an array (an array containing the entered numbers), you can use JSON.
When loading, you need to add the numbers just like happens when the user fills them in. So you can set the name input box value to the saved number for each element in the array, and then simulate a click on the Add button.
So you need an add function that is executed when:
User clicks Add button
Page is loaded
However, when simulating the click the numbers should not get stored again. You need to distinguish between a real click and a simulated one. You can accomplish this by adding an argument to the add function which represents whether or not to store.
Not entirely sure what the question is, but one problem I see with the code - id's can't be numbers, or start with numbers
var nameOfnames = form1.getElementsByClassName("inputNames").length;
//....
newGuy1.setAttribute("id", nameOfnames);
That might be slowing you down somewhat. Perhaps set id to 'newguy' + nameOfnames
Jeff, the reason that the page keeps getting erased is because the form submission triggers a page reload. You need to place a listener on the form submit event and then send the data through AJAX. This way the data is POSTed to "text.php" without reloading the page with the form.
You could place the values in a cookie but that is not ideal because you have a fairly limited amount of space to work with (4kb). I also get the feeling that you're trying to hand them off to some server side script so HTML5 local storge wouldnt be a good solution, not to mention that your eliminating over half of the people on the internet from using your site that way.
Since browsers are inconsistent in how they attach event listeners AND how they make AJAX requests. I think that most people would recommend that you use a library like jQuery, dojo, or prototype which abstract the process into one function that works in all browsers. (my personal fav is jQuery)
There are a few options available to you:
Save it client side using cookies (http://www.quirksmode.org/js/cookies.html)
Save it client side using HTML5 local storage (http://diveintohtml5.ep.io/storage.html)
Save it server-side using Ajax
The Ajax solution involves a server side page (in PHP for example) that reads a request (a POST request for example) and saves it into a database or other. You then query that page in JavaScript using XmlHTTPRequest or your favorite library.

Categories

Resources