You cannot apply bindings multiple times to the same element - javascript

I have a Bootstrap modal, and every time it shows up I will use KO to bind a <select> dropdown.
HTML:
<select id="album" name="album" class="form-control" data-bind="options: availableAlbums">
</select>
JavaScript:
$('#uploadModal').on('show.bs.modal', (function () {
function AlbumsListViewModel() {
var self = this;
self.availableAlbums = ko.observableArray([]);
$.ajax({
url: "../../api/eventapi/getalbums",
type: "get",
contentType: "application/json",
async: false,
success: function (data) {
var array = [];
$.each(data, function (index, value) {
array.push(value.Title);
});
self.availableAlbums(array);
}
});
}
ko.applyBindings(new AlbumsListViewModel());
}));
However, on the second showing, KO will present me with this error:
Error: You cannot apply bindings multiple times to the same element.

The error message says most of it. You have two options:
Call the applyBindings function once, when your page loads. KO will automatically update the View when you update the model in a AJAX success function.
Call the applyBIndings function on each AJAX success, but supply additional parameters to tell it what element to bind to.
Most likely the first option is what you're looking for. Remove the call from the $('#uploadModal').on call and place it on document load (if you haven't already).
To see what I mean, here's two fiddles:
Your current code with the error you mention.
Refactored version that doesn't have the error.
The latter tries to stay as close as possible to your initial version (so as to focus on the problem at hand), and goes along these lines:
function AlbumsListViewModel() {
var self = this;
self.availableAlbums = ko.observableArray([]);
}
var mainViewModel = new AlbumsListViewModel();
ko.applyBindings(mainViewModel);
$('#uploadModal').on('show.bs.modal', (function () {
// Commenting things out to mock the ajax request (synchronously)
var data = [{Title:'test'}];
/*$.ajax({
url: "../../api/eventapi/getalbums",
type: "get",
contentType: "application/json",
async: false,
success: function (data) {*/
mainViewModel.availableAlbums.removeAll();
var array = [];
$.each(data, function (index, value) {
array.push(value.Title);
});
mainViewModel.availableAlbums(array);
/*}
});*/
}));

Related

jQuery: How to get input value within an ajax call?

I have a jQuery script, in which I need to load data from a CSV from an external URL.
At the same time, I need to combine the data from the CSV with data provided by a frontend user through an input field. My expected result would be that I'd be able to call the jQuery code for fetching the value from the user's entry into the input field. However, while this works when the code is placed outside the ajax call, it does not work inside.
At the same time, I can't place the code for fetching the user's input outside the ajax call, as I need to be able to utilize information from the loaded CSV together with the user input to perform a dynamic calculation.
My code is as below:
HTML:
<input type="text" id="my_input" value="12" maxlength="2">
Javascript:
$.ajax({
url: csv_url,
async: false,
success: function (csvd) {
var csv = $.csv.toObjects(csvd);
// XYZ done with the CSV data to populate various fields
$("#my_input").keyup(function () {
console.log(this.value);
// this does not result in anything being logged
});
},
dataType: "text",
complete: function () {}
});
Of course it will not work. You are telling the compiler to add the keyup event listener to input after the ajax call is successful. Which means it will not work until ajax call is completed successfully.
You need to put the keyup event listener outside and inside the ajax call just get the value like below:
let myInputValue = "";
$("#my_input").keyup(function () {
console.log(this.value);
myInputValue = this.value;
});
$.ajax({
url: csv_url,
async: false,
success: function (csvd) {
var csv = $.csv.toObjects(csvd);
// XYZ done with the CSV data to populate various fields
//And later use the myInputValue here
},
dataType: "text",
complete: function () {}
});
You have not give us enough info. if you only need the current value in the ajax call you can do like below:
$.ajax({
url: csv_url,
async: false,
success: function (csvd) {
var csv = $.csv.toObjects(csvd);
// XYZ done with the CSV data to populate various fields
let myInputValue = $("#my_input").val();//And later use the myInputValue here
},
dataType: "text",
complete: function () {}
});

Data not binding to Select on first Ajax call

EDIT:
One unusual thing that i notice is that if i select an option from parent SELECT (which fires AJAX call to bring data for binding the next SELECT ) , if i immediately try to open it it continues to show no options but if I wait for couple of seconds , I am able to see the options !
I am trying AngularJS for first time and facing a problem wherein the SELECT control is not binding on FIRST Ajax call but displays item when AJAX call is done second time. The AJAX is fired on change event of another drop down
var app = angular.module('MyApp', []);
app.controller('MyController', function ($scope, $window) {
$scope.singleSelect = ' ';
$scope.IsVisible = false;
$scope.GetValue = function () {
$scope.IsVisible = true;
$scope.DefaultLabel = "Loading.....";
$.ajax({
type: "POST",
url: "../ContentPages/frmManualAccess.aspx/BindDelegationLevel",
data: "{pDelegationLevel :'ALL'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
console.log(JSON.parse(msg.d));
/* Gives array of 3 objects
[{"DelegationCode":"Customer","DelegationName":"Customer"},
{"DelegationCode":"Segment","DelegationName":"Segment"},
{"DelegationCode":"SBUM","DelegationName":"Customer"}]
*/
$scope.myOptions = JSON.parse(msg.d);
$scope.defaultDelegationLevel = $scope.myOptions[0].DelegationCode;
},
error: function (xhr, errorType, exception) {
var varResponseText = jQuery.parseJSON(xhr.responseText);
alert(varResponseText.Message);
}
});
}
});
And in HTML the SELECT i am trying to bind is below
<select ng-model="defaultDelegationLevel"
ng-options="option.DelegationName as option.DelegationCode for option in myOptions" >
</select>
I dont know why its happening , please help in this issue. Also an empty option appears as first option that i am trying to remove.

Async ajax call causing browser to freeze

I have two different sets of a and p elements in my html page which are made as display:none by default.
At the end of the of the page I'm calling a function by sending their ID's and some values to enable any one of them based on some conditions
1st set
<a style="display:none;" id="ClickMe1">Click Me</a>
<p class="button" id="Sorry1" style="display:none;">Sorry!</p>
2nd set
<a style="display:none;" id="ClickMe2">Click Me</a>
<p class="button" id="Sorry2" style="display:none;">Sorry!</p>
Function call
<script>
window.onload = function () {
Initialize("ClickMe1", "Sorry1", "23,35");
Initialize("ClickMe2", "Sorry2", "76,121");
};
</script>
Initialize function consists of a ID, p ID and set of values(it can contain n values) to check which element to enable
Javascript Function
function Initialize(ClickMeID, SorryID,Values) {
var valList = Values.split(',');
for (i = 0; i < valList.length; i++) {
var paramts = "{'val':'" + valList[i] + "'}";
jQuery.ajax({
type: "POST",
url: "/services/MyService.asmx/GetData",
data: paramts,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
success: function (response) {
Status = response.d.toString();
},
error: function (response) {
}
});
if (Status == "0") {
$('#' + SorryID).show();
return;
}
}
$('#' + (ClickMeID).show();
}
In my function I'm splitting the comma seperated Values and looping through each value and making an ajax call to my service with async:false.
The response of success call is either 1 or 0. If any of Values is 0 of a function call I want to display p element else a element of the sent ID's.
This function is working fine but when the function call is raised this is making the browser freeze until the execution of the function.
If I make async: true I'm not able to find out which set of buttons to enable and disable
How can I make prevent the browser from freezing.
You should set
async: true
If it's not async, then it'll be blocking.
Also, if you're looping through many items, you should wrap each iteration in a setTimeout and make it async too.
Code samples
function click(e){
var button = e.target;
$.ajax({
type: "POST",
url: "http://localhost/accounts/save",
data : {
accountID: 123,
name:"hello world"
},
beforeSend: function(){
//disable the button.
}
}).always(function(){
//enable the button
})
}
here's an example of of setTimeout
setTimeout(function(){
//do something
}, 3000); //3seconds
I would highly recommend, that you read up on jquery.Deferred and event loops.
I'm not able to find out which set of buttons to enable and disable
Then that is your real issue. You solved your problem with other code to cause a new problem.
I highly suggest reading Decoupling Your HTML, CSS, and JavaScript.
Here is what I would do (since you tagged jquery might as well.. actually fully use it).
<style>
.is-hidden{ display: none; }
</style>
<div class="js-init-content" data-params="[{'val':23},{'val':35}]">
<a class="is-hidden js-clickme">Click Me</a>
<p class="button is-hidden js-sorry">Sorry!</p>
</div>
<div class="js-init-content" data-params="[{'val':76},{'val':121}]">
<a class="is-hidden js-clickme">Click Me</a>
<p class="button is-hidden js-sorry">Sorry!</p>
</div>
<script>
// when the document is ready...
$(document).ready(function(){
// loop through each init-content item
$(".js-init-content").each(function(){
var $this = $(this);
// get the data from the html element
// jquery will return an array containing objects
// because it's smart and cool like that
var params = $this.data('params');
var isAvailable = true;
// loop through each param
$.each(params, function(index, param){
// stop loop and ajax calls if any previous ajax call failed
if (!isAvailable) return false;
// make an ajax call, param will be the object from the array
$.ajax({
type: "POST",
url: "/services/MyService.asmx/GetData",
data: param,
contentType: "application/json; charset=utf-8",
// dataType: "json", -- jquery is smart it will figure it out
// async: false, -- Almost no reason to ever do this
).done(function(response){
isAvailable = response.d.toString() != "0";
}); // End Ajax-Done
}); // End js-init-content.each
var selector = isAvailable
? ".js-clickme"
: ".js-sorry";
$this.find(selector).removeClass("is-hidden");
}); // End doc-ready
</script>
I encapsulated the data in the html, instead of hardcoding it in the javascript. Fully used jQuery for loading and updating.

Binding table in MVC 4 after Ajax call

I have an HTML able, which I bind by using the following Action in MVC controller:
public ActionResult BindTable(int ? page)
{
int pageSize = 4;
int pageNumber = 0;
List<Users> _users = query.ToList();
return View(_users.ToPagedList(pageNumber, pageSize));
}
Below the table I have the following HTML:
<textarea class="form-control" style="resize:none;" rows="9" placeholder="Enter value here..." id="txtValue"></textarea>
<br />
<button style="float:right; width:100px;" type="button" onclick="CallFunction()" class="btn btn-primary">Update specific record</button>
The Javascript function responsible for calling the action is as following:
function CallFunction() {
if ($('#txtValue').val() !== '') {
$.ajax({
url: '/User/UpdateUser',
type: 'POST',
data: { txt: $('#txtValue').val() },
success: function (data) {
$('#txtValue').val('');
alert('User updated!');
},
error: function (error) {
alert('Error: ' + error);
}
});
}
And here is the Action responsible for updating the user:
public ActionResult UpdateUser(string txtValue)
{
var obj = db.Odsutnost.Find(Convert.ToInt32(1));
if(obj!=null)
{
obj.Text= txtValue;
obj.Changed = true;
db.SaveChanges();
return RedirectToAction("BindTable");
}
return RedirectToAction("BindTable");
}
Everything works fine. But the table doesn't updates once the changes have been made ( it doesn't binds ?? )...
Can someone help me with this ???
P.S. It binds if I refresh the website.. But I want it to bind without refreshing the website...
I created a BIND function with Javascript, but it still doesn't binds:
function Bind() {
$(document).ready(function () {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
});
});
}
You're not actually updating the page after receiving the AJAX response. This is your success function:
function (data) {
$('#txtValue').val('');
alert('User updated!');
}
So you empty an input and show an alert, but nowhere do you modify the table in any way.
Given that the ActionResult being returned is a redirect, JavaScript is likely to quietly ignore that. If you return data, you can write JavaScript to update the HTML with the new data. Or if you return a partial view (or even a page from which you can select specific content) then you can replace the table with the updated content from the server.
But basically you have to do something to update the content on the page.
In response to your edit:
You create a function:
function Bind() {
//...
}
But you don't call it anywhere. Maybe you mean to call it in the success callback?:
function (data) {
$('#txtValue').val('');
Bind();
alert('User updated!');
}
Additionally, however, that function doesn't actually do anything. For starters, all it does is set a document ready handler:
$(document).ready(function () {
//...
});
But the document is already loaded. That ready event isn't going to fire again. So perhaps you meant to just run the code immediately instead of at that event?:
function Bind() {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
});
}
But even then, you're still back to the original problem... You don't do anything with the response. This AJAX call doesn't even have a success callback, so nothing happens when it finishes. I guess you meant to add one?:
function Bind() {
var serviceURL = '/User/BindTable';
$.ajax({
type: "GET",
url: serviceURL,
contentType: "application/json; charset=utf-8",
success: function (data) {
// do something with the response here
}
});
}
What you do with the response is up to you. For example, if the response is a completely new HTML table then you can replace the existing one with the new one:
$('#someParentElement').html(data);
Though since you're not passing any data or doing anything more than a simple GET request, you might as well simplify the whole thing to just a call to .load(). Something like this:
$('#someParentElement').load('/User/BindTable');
(Basically just use this inside of your first success callback, so you don't need that whole Bind() function at all.)
That encapsulates the entire GET request of the second AJAX call you're making, as well as replaces the target element with the response from that request. (With the added benefit that if the request contains more markup than you want to use in that element, you can add jQuery selectors directly to the call to .load() to filter down to just what you want.)

Loading data into a field with jQuery AJAX

I have this code below which is called by running the getGrades function.
function getGrades(grading_company) {
// Set file to get results from..
var loadUrl = "ajax_files/get_grades.php";
// Set data string
var dataString = 'gc_id=' + grading_company;
// Set the callback function to run on success
var callback = showGradesBox;
// Run the AJAX request
runAjax(loadUrl, dataString, callback);
}
function showGradesBox(response) {
// Load data into grade field
// Hide condition fields
jQuery('#condition').hide();
jQuery('#condition_text').hide();
// Show grade fields
jQuery('#grade_wrapper').show();
jQuery('#grade_text_wrapper').show();
}
function runAjax(loadUrl, dataString, callback) {
jQuery.ajax({
type: 'GET',
url: loadUrl,
data: dataString,
dataType: 'html',
error: ajaxError,
success: function(response) {
callback(response);
}
});
}
Now as you can see I am passing the AJAX response data to the showGradesBox function; however I'm now not sure how to load it into the field.
I have seen example using .load() but it seems you have to use this with the URL all at once; the only other function I have come across that I could possibly use is .html(); but the description of it doesn't sound right!?
.html() should work ...
When .html() is used to set an element's content, any content that was in that element is completely replaced by the new content. Additionally, jQuery removes other constructs such as data and event handlers from child elements before replacing those elements with the new content.
function showGradesBox(response) {
// Load data into grade field
jQuery('#yourgradefieldID').html(response);
// Hide condition fields
jQuery('#condition').hide();
jQuery('#condition_text').hide();
// Show grade fields
jQuery('#grade_wrapper').show();
jQuery('#grade_text_wrapper').show();
}
Assuming a field with ID grade_text and a return of a string from the PHP:
function showGradesBox(response) {
// Load data into grade field
jQuery('#grade_text').val(response);
// Hide condition fields
jQuery('#condition').hide();
jQuery('#condition_text').hide();
// Show grade fields
jQuery('#grade_wrapper').show();
jQuery('#grade_text_wrapper').show();
}
This assigns a value of 'undefined' to your callback.
// Set the callback function to run on success
var callback = showGradesBox;
Try assigning the function to a variable named showGradesBox before your functions like this
var showGradesBox = function(response) {
// Load data into grade field
// Hide condition fields
jQuery('#condition').hide();
jQuery('#condition_text').hide();
// Show grade fields
jQuery('#grade_wrapper').show();
jQuery('#grade_text_wrapper').show();
}
function getGrades(grading_company) {
// Set file to get results from..
var loadUrl = "ajax_files/get_grades.php";
// Set data string
var dataString = 'gc_id=' + grading_company;
// Set the callback function to run on success
var callback = showGradesBox;
// Run the AJAX request
runAjax(loadUrl, dataString, callback);
}
function runAjax(loadUrl, dataString, callback) {
jQuery.ajax({
type: 'GET',
url: loadUrl,
data: dataString,
dataType: 'html',
error: ajaxError,
success: function(response) {
callback(response);
}
});
}

Categories