closure loop in ajax callback - javascript

i have a data which were parsed from json, i display the data in a box like facebook's friends suggestion box. i want when the user click on any of the suggested users the request to be added to DB via ajax and its corresponding button disappears,everything is working just fine except the last thing(button disappears) instead the very first button in the list gets disappear, while im searching for a solution to my problem i came across something called closure but i reaaly couldn't know how to implement it in my code, another problem appeared when i tried to declare the listener anonymous function inside the loop was the data get inserted in the DB multiple times
(because its inside a loop), i know it might seems duplicated question but i just need someone pointing me the right place to declare my inner function,
my code looks like this
$(document).ready(function() {
var suggest = new XMLHttpRequest();
suggest.onreadystatechange = function() {
if (suggest.readyState === 4 && suggest.status === 200) {
var susers = JSON.parse(suggest.responseText);
for (var i = 0; i < susers.length; i += 1) {
var sphoto = '<div class="col-md-4 text-left "> <div id="fimage">';
sphoto += '<img width="50" height="50" src="user/';
sphoto += susers[i].activation + '/' + susers[i].pic + '"> </div> </div>';
var sname = '<div id="fname">' + susers[i].name + '</div>';
// here is the form im targetting to pull informtion from
var hidden = '<form id="fform"><input id="fnameh" name="name" type="hidden" value="' + susers[i].name + '" >';
hidden += '<input name="id" type="hidden" value="' + susers[i].id + '" >';
var fbutton = '<button id="addfriend"class="btn btn-info btn-xs pull-right text-center" type="submit" >Follow <span class=" glyphicon glyphicon-plus" aria-hidden="true"></span> </button></form';
var display = document.getElementById('fsuggest');
display.innerHTML += '<div class="scroller"><div id="fspace" > <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>' + sphoto + sname + hidden + fbutton +'</div></div>'
$('#addfriend').live('click', function(arg) {
return function() {
arg.preventDefault();
var data = $('#fform').serialize();
$.ajax({
data: data,
type: "post",
url: "addnew.php",
success: function(data) {
//make the text area empty
$('#addfriend').css("display", "none");
console.log(data);
}
}); // end of $.ajax
}(i); // end of inner function
}); // end of click listner
} //end of for loop
}
};
suggest.open('GET', 'box.php');
suggest.send();
}); // end of JQUERY ready

I am not sure why exactly you need closure here. The problem seems to be with the multiple form & button with same id.
All these buttons have same id so the forms. So
$('#addfriend').live('click', function(arg) {..}) has no reference to the particular button
Hope this change will help
Con-cat the value of iwith the id of the form & add an attribute data-id=i the button.
Beside instead of id add class addfriend to the button. So when this button will be clicked take the data-id value. Use this value to get the relevant form with id , & serialize that
var hidden = '<form id="fform_' + i + '"><input id="fnameh" name="name" type="hidden" value="' + susers[i].name + '" >';
hidden += '<input name="id" type="hidden" value="' + susers[i].id + '" >';
var fbutton = '<button data-id="' + i + '" id=""class=" addfriend btn btn-info btn-xs pull-right text-center" type="submit" >Follow <span class=" glyphicon glyphicon-plus" aria-hidden="true"></span> </button>';
Make below change to the click function.
Also reason of adding the event handler inside loop is not clear.
$('.addfriend').live('click', function(arg) {
arg.preventDefault();
var getDataId = $(this).attr('data-id') // get the data-id
var data = $('#fform_'+getDataId).serialize(); // get relevant form
$.ajax({
// rest of code
}); // end of $.ajax
})
Try to use jquery version above 1.9 to use the on method for event delegation

I made a few changes to your code.
With the new javascript, I believe most browsers support string interpolation natively (without any extra plugins or pre-compiling with babel for example) so you can just interpolate your variables within backticks ``.
Also, you seemed to be creating a form with the id of #fform and a button with the id of #addfriend for each iteration. The problem is that calling $('#fform') or $('#addfriend') will return an array with as many elements as you have users.
So I added an extra data-suser-id attribute with unique id's on those. I am not sure the code will work because I can't actually try it and changed quite a few things but let me know if you are getting closer to your solution.
$(document).ready(function() {
var suggest = new XMLHttpRequest();
suggest.onreadystatechange = function() {
if (suggest.readyState === 4 && suggest.status === 200) {
var susers = JSON.parse(suggest.responseText);
var limit = susers.length;
for (var i = 0; i < limit; i += 1) {
var sphoto = `<div class="col-md-4 text-left ">
<div id="fimage">
<img width="50" height="50" src="user/${susers[i].activation}/${susers[i].pic}">
</div>
</div>`
var sname = `<div id="fname">${susers[i].name}</div>`
// here is the form im targetting to pull informtion from
var hidden = `<form id="fform" data-suser-id='${susers[i].id}'>
<input id="fnameh" name="name" type="hidden" value="${susers[i].name}" >
<input name="id" type="hidden" value="${susers[i].id}">`
var mutalusers = `</form><div id="mutal" class="text-left">
<h6>
<div id="fmutal">
<?php echo $c = mutal(${susers[i].id}, $me, $db) ?>
</div>
</h6>
</div>`
var fbutton = `<button id="addfriend" class="btn btn-info btn-xs pull-right text-center" type="submit" data-suser-id='${susers[i].id}'>Follow <span class=" glyphicon glyphicon-plus" aria-hidden="true"></span> </button>`;
var display = document.getElementById('fsuggest');
display.innerHTML += `<div class="scroller">
<div id="fspace">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
${sphoto}
${sname}
${hidden}
${fbutton}
${mutalusers}
</div>
</div>`
// #live method is deprecated : http://api.jquery.com/live/
var addfriend_button = $(`#addfriend[data-suser-id=${susers[i].id}]`)
addfriend_button.on('click', function(arg) {
// return function(){
arg.preventDefault();
// the selector `#fform` doesn't seem to be unique to the current user's form data, so I added an data-attribute to make sure we are getting the correct form data
var data = $(`#fform[data-suser-id=${susers[i].id}]`).serialize();
$.ajax({
data: data,
type: "post",
url: "addnew.php",
success: function(data) {
//make the text area empty for the current user's `#addfriend` button
addfriend_button.html('done');
console.log(data);
}
}); // end of $.ajax
// }(i); // end of inner function : not sure why this inner function is necessary
}); // end of click listner
} //end of for loop
console.log(susers);
}
};
var suugestFile = 'box.php';
suggest.open('GET', suugestFile);
suggest.send();
}); // end of JQUERY ready

Related

When dynamically creating input fields, <select> filled with $.each only once, why?

I try to dynamically add an input field to a form. It works fine except one thing:
I try to fill a with $.each, but this only works for the first dynamically added if I add more, the stays empty..
the #add button is in the initial form:
append:
$('#add').click(function() {
i++;
$dynamic_field').append('' +
'<h1>Sensor '+i+'</h1> ' +
'<tr id="row'+i+'">
'<td><div class="form-group">\n' +
'<label for="InputSensorType">Sensor Type*</label>\n' +
'<select class="form-control" id="InputSensorType" name="sensorType[]"></select>\n'
'</div></td>\n +'
'<td>
<button type="button" name="copy" id="copy" class="btn btn-primary">Copy this sensor
</button></td><tr>');
Each:
var jsarray = <?php echo json_encode($sensors)?>;
$.each(jsarray, function (index, value) {
$('#InputSensorType').append(('<option value='+index+'>'+value+'</option>'));
});
Can anyone help me?
Try this
var jsarray = '<?php echo json_encode($sensors)?>';
var obj = jQuery.parseJSON(jsarray);
var data = '';
$.each(jsarray, function (index, value) {
data +='<option value='+index+'>'+value+'</option>';
});
$('#InputSensorType').append(data);

JavaScript function not getting called?

So I have a button that calls
<a class="btn btn-primary" id="btnAdd" href="#" onclick="ChangesJs.AddPlayList()"> <i class="fa fa-fw fa-plus fa-lg"></i>Add </a>
and the JS function creates additional form on its own.
function AddPlayList() {
var form = "<div class='form-group col-sm-3 clscommercial_" + addPlayList + "' style='display:none;' ><label>Break No.</label> <span class='red_color'>*</span><input class='form-control' id='txtBreakno_" + x + "' maxlength='2' onblur='ChangesJS.IsNumeric(this)' onchange='CommonJs.HideErrorMessage(this)' placeholder='Break No.' type='text'></div>";
This is the definition of IsNumeric function
function IsNumeric(selectinput) {
var _value = selectinput.value;
var ID = selectinput.id;
if (_value !== "" && !$.isNumeric(_value)) {
$("#div_" + ID).show();
$("#span_" + ID).html("Please Enter numeric value !");
selectinput.value = "";
selectinput.focus();
}
}
When I get of out focus in the text field no validation is shown.
The elements created in the dom after initial load need to have an event listener added.
function AddPlayList() {
var form = "<div class='form-group col-sm-3 clscommercial_" + addPlayList + "' style='display:none;' ><label>Break No.</label> <span class='red_color'>* </span><input class='form-control' id='txtBreakno_" + x + "' maxlength='2' onblur='ChangesJS.IsNumeric(this)' placeholder='Break No.' type='text'></div>";
// append it to the DOM....
var element = document.getElementsByClassName("clscommercial_" + addPlayList);
element.addEventListener('change', function() {
CommonJs.HideErrorMessage(this);
}, false);
}
Also, don't forget to remove the listener if you remove the element it you may end up having it fire multiple times.
The jQuery way handles this well if your using it.
$('body').on('change', '.clscommercial', function() {
// This will fire even on elements added programmatically as long
// as your top selector is (body) was created on page load.
CommonJs.HideErrorMessage($(this)[0]);
)};

JS: Calling function on Submit

Hello everyone I'm still new to JS, so I want to ask about calling a function when form is submitted.
[update] Form
<div id="dashboard-side">
<form id="report" class="form-horizontal" method="post" action="<?= site_url('api/load_report') ?>"> <!-- onsubmit="location.reload()" -->
<fieldset>
<div class="control-group center_div">
<label class="control-label">Sales Name</label>
<div class="controls">
<input type="text" name="sales_name" class="form-control input-xlarge txt-up" value="<?php echo set_value('cust_name'); ?>" placeholder="Enter Customer Name" style="height: 30px;"/>
</div>
</div>
<div class="control-group center_div">
<div class="controls form-inline">
<input id="get_report" type="submit" class="btn btn-success btn-inline " value="Get Report" style="width:110px; margin-left: -155px;"/>
</div>
</div>
<table border="1" width="100%" style="background-color: #dfe8f6;">
<tr>
<td width="154px"><strong>Customer Name</strong></td><td width="128px"><strong>Activity</strong></td>
<td width="244px"><strong>Detail</strong></td><td width="141px"><strong>Start Time</strong></td>
<td width="142px"><strong>Finish Time</strong></td><td width="39px" style="text-align:center"><strong>Note</strong></td>
<td style="margin-left: 50px"><strong>Action</strong></td>
</tr>
</table>
<!------------------------------------------------------------------------------------->
<div id="xreport" class="table-hover" style="background-color: #EAF2F5"></div>
</fieldset>
</form>
</div>
Controller
public function load_report() {
$this->db->where('user_id', $this->input->post('sales_name'));
$query = $this->db->get('activity');
$result = $query->result_array();
$this->output->set_output(json_encode($result)); }
JS
var load_report = function() {
$.get('api/load_report', function(o){
var output = '';
for (var i = 0; i < o.length; i++){
output += Template.dodo(o[i]);
}
$("#xreport").html(output);
}, 'json');
};
If I call the function on form load it works fine, but I want to call it on form submit, how to do that?
Here is what I tried
var load_report = function () {
$("#report").submit(function(){
$.get('api/load_report', function(o){
var output = '';
for (var i = 0; i < o.length; i++){
output += Template.dodo(o[i]);
}
$("#xreport").html(output);
}, 'json');
});
};
Instead of assigning the array into my #div, it shows the array data in the new blank tab like this:
my current result so far
any help would be appreciated, thanks.
Update: New calling function
var load_report = function () {
$("#report").submit(function (evt) {
evt.preventDefault();
var url = $(this).attr('action');
var postData = $(this).serialize();
$.post(url, postData, function (o) {
if (o.result == 1) {
var output = '';
Result.success('Clocked-in');
for (var i = 0; i < o.length; i++) {
output += Template.dodo(o[i]); //this data[0] taken from array in api/load_report
console.log(output);
$("#xreport").html(output);
}
} else {
Result.error(o.error);
console.log(o.error);
}
}, 'json');
});
};
with this new calling function I'm able to retrieve data from api/load_report without getting stuck on e.preventDefault or even open a new tab, I console.log and the result show correctly in the console, but it doesn't show on the div somehow.
my template.js (if needed)
this.dodo = function(obj){
var output ='';
output +='<table border=1, width=100%, style="margin-left: 0%"';
output += '<tr>';
output += '<td width=120px>' + obj.user_id + '</td>';
output += '<td width=120px>' + obj.cust_name + '</td>';
output += '<td width=100px>' + obj.act_type + '</td>';
output += '<td width=190px>' + obj.act_detail + '</td>';
output += '<td width=110px>' + obj.date_added + '</td>';
output += '<td width=110px>' + obj.date_modified + '</td>';
output += '<td style="text-align:center" width=30px>' + obj.act_notes + '</td>';
output += '</tr>';
output +='</table>';
output += '</div>';
return output;
};
Result (note, user_id = form.sales_name)
result preview
First off, I would advise against using onclick or onsubmit directly on the dom like so.
<form onsubmit="myFunction();"></form>
The biggest reason for me is that it negatively impacts readability and sometimes even future maintenance of the project . It is nice to keep html and javascript separate unless you are using a framework that provides templating features/functionality such as angularjs.
Another big one is that you can only have one in-line event present and it is easy to accidentally overwrite, which can lead to confusing bugs.
The reason it is going to a new tab is because of the .submit(). This will call your function, but then proceed with internal jQuery event handling which includes refreshing the page. There are two solutions I see most viable here.
1st Solution:
Add a event.preventDefault(); at the start of your function to stop jQuery from refreshing your page.
$("#report").submit(function(e) {
e.preventDefault();
});
2nd Solution (Likely better):
You are making an ajax call with $.get(...). You could add an event listener on a button (probably on the button used to submit the form) that fires your ajax call. Here is an example that assumes the submit button has the id of loadData.
$("#loadData").click(function(){
$.get('api/load_report', function(o){
var output = '';
for (var i = 0; i < o.length; i++){
output += Template.dodo(o[i]);
}
$("#xreport").html(output);
}, 'json');
}
You could alway do the following:
function myFunction() {
alert("Hello! I am an alert box! in a function");
}
<form>
<input type="text" />
<input type="submit" value="submit" onclick="return myFunction();"/>
</form>
P.S. This question might be answered in this question already, add onclick function to a submit button
Try to prevent form to get submit by using preventDefault()
$("#report").submit(function(e){ //add param "e"
e.preventDefault(); //to prevent form to submit
//further code....
});
Ok just try
function mysubmit(){
$.get('api/load_report', function(o){
var output = '';
for (var i = 0; i < o.length; i++){
output += Template.dodo(o[i]);
}
$("#xreport").html(output);
}, 'json');
}
as your script and
<form id="report" class="form-horizontal" onsubmit="event.preventDefault(); mysubmit();" method="post" action="<?= site_url('api/load_report') ?>">
as your form

Same function for different sections - relative referencing in jquery

By using relative references I am able to remove items which have been added to the list within a specfic part of the form. For example, by adding a requirement it can be deleted just from the requirement.
My issue is two fold:
Adding an item to references adds it to all three categories
When I try to add values to the other sections (qualifications) it says my input was blank.
http://jsfiddle.net/spadez/9sX6X/60/
var container = $('.copies'),
value_src = $('#current'),
maxFields = 10,
currentFields = 1;
$('.form').on('click', '.add', function () {
value_src.focus();
if ($.trim(value_src.val()) != '') {
if (currentFields < maxFields) {
var value = value_src.val();
var html = '<div class="line">' +
'<input id="accepted" type="text" value="' + value + '" />' +
'<input type="button" value="X" class="remove" />' +
'</div>';
$(html).appendTo(container);
value_src.val('');
currentFields++;
} else {
alert("You tried to add a field when there are already " + maxFields);
}
} else {
alert("You didn't enter anything");
}
})
.on('click', '.remove', function () {
value_src.focus();
$(this).parents('.line').remove();
currentFields--;
});
Is it possible to modify this code without repeating it for each section, by using relatively references such as "parent" for example. I want to use this same script for all three sections but have it so each list is independant.
I'm new to javascript so I was wondering if this is possible because I only managed to get it working on the delete.
You have to use this to get the current element. In your case this refers to the button which was clicked.
The next step is to get the input box which belongs to the button. E.g. $(this).prev(); like in this example:
$('.form').on('click', '.add', function () {
var value_src = $(this).prev();
http://jsfiddle.net/9sX6X/62/
The same is also true for your appending part. Your are appending your html to all three elements which match $('.copies'). Instead you have to try to get there from this.
$('.form').on('click', '.add', function () {
var value_src = $(this).prev();
var copies = $(this).parent().prev();
http://jsfiddle.net/9sX6X/63/
I would suggest adding a wrapping div to each section.
<div class="section">
<h4>Requirements</h4>
<div class="copies"></div>
<div class="line">
<input id="current" type="text" name="content" placeholder="Requirement" />
<input type="button" value="Add" class="add" />
</div>
</div>
Then you can do this:
var $section = $(this).closest(".section");
$(html).appendTo($section.find(".copies"));
This will add to just the related .copies element instead of to all .copies as your code does now. A similar approach can be used for all other elements as well.

Element is not defined jQuery $(element).remove()

I have this JavaScript that adds a form field, along with a link to remove that field:
var fieldCount = 0;
function addField() {
var name = 'file' + fieldCount;
var row = 'row' + fieldCount;
var str = '<p id="' + row + '"><label for="' + name + '">File to upload: <input type="file" name="' + name + '" id="' + name + '" />(100MB max size) <a onclick="removeRow(' + row + '); return false;">[-]</a></label></p>';
fieldCount++;
$("#fields").append(str);
};
function removeRow(id) {
$(id).remove();
};
Here is the markup:
<form id="ajaxUploadForm" action="<%= Url.Action("AjaxUpload", "Upload")%>" method="post" enctype="multipart/form-data">
<fieldset id="uploadFields">
<legend>Upload a file</legend>
<div id="fields"></div>
<input id="ajaxUploadButton" type="submit" value="Submit" />
</fieldset>
<a onclick="addField(); return false;" id="add">Add</a>
<div id="resultBox">
<p id="status" style="margin:10px;"></p>
</div>
</form>
The addFields works as expected, but when I click the remove link firebug tells me that row# is not defined, where # is any number of the added fields.
Any help would be appreciated!
You need to pass a valid selector expression for an ID selector (#ID), either in the removeRow call (also note the quotes surrounding the ID selector):
'<a onclick="removeRow(\'#' + row + '\'); return false;">'
Or in the removeRow function itself:
function removeRow(id) {
$("#" + id).remove();
};
You need to have quotes around it, since it's a string.
You also need the "#" to make it into a selector:
var str = '... <a onclick="removeRow(\'#' + row + '\'); return false;">...';
A better way would be to assign the onclick as a function (not sure of the jQuery way to do this but in plain Javascript):
var a = document.createElement('a');
a.onclick = (function(row)
{
return function()
{
removeRow(row);
return false;
};
})();
You are passing in the string value of row12, but the selector should be:
$('#'+row).remove()
The # specifies that you are looking for an ID. I agree with what I think one of the other answers was about to say, you should just use the onclick events natural this keyword instead:
<p onclick="remove(this)">something</p>
function remove(what) {
$(what).remove()
}
Or, maybe just forget the whole thing all together and switch to behavior for those kinds of rows:
$('.removableRow').live('click', function() {$(this).remove()});
Then you just specify that the row is removable, and never have to worry about binding events at all:
<p><a class="removableRow" href="#">Remove</a></p>

Categories