AJAX not working with Knockout.JS - javascript

For some reason, I cannot get an AJAX function
$('#queueSubmit').on('click', function() {
$.ajax({
type: 'POST',
url: nodeApiUrl + 'twitter/update_status/',
contentType: 'application/json',
data: JSON.stringify({'status': $('#queuedTweet').val() }),
dataType: 'json',
error: $.osf.handleJSONError
});
});
to be called when I click the button with id queueSubmit. This is the knockout code that generates the buttons and content.
<div id = "foo">
<!-- ko foreach: tweets -->
<input id = "queuedTweet" data-bind="value: tweet"/>
<a class="btn btn-primary" id = "queueSubmit" >
Send
</a>
<a id = "removeFromQueue" data-bind = "click: $parent.removeTweet, value:index" class="btn btn-danger">
Delete
</a>
</br>
<!-- /ko -->
</div>
It does not register that the button is being clicked. I believe it has something to do with the bindings but not sure how to remedy. Any help would be appreciated!

You want do do this instead of your current implementation:
<a class="btn btn-primary" data-bind="click: $parent.queueSubmit">Send</a>
Then in your viewModel do this:
self.queueSubmit = function() {
$.ajax({
type: 'POST',
url: nodeApiUrl + 'twitter/update_status/',
contentType: 'application/json',
data: JSON.stringify({'status': $('#queuedTweet').val() }),
dataType: 'json',
error: $.osf.handleJSONError
});
};
Use knockout bindings any time you can. If a native binding doesn't exist, create a custom binding.

IDs are supposed to be unique, but you are creating them within a ko foreach loop. That means you actually have an array of "queueSubmit" IDs. jQuery assumes you will only have one, so it always retrieves the first one in the list.
You can see this in action by using the native JavaScript functions:
document.querySelector("#queueSubmit"); // <== returns the first one in the list
document.querySelectorAll("#queueSubmit"); // <== return the array of elements with that id
document.querySelectorAll("#queueSubmit")[n]; // <== returns the element at index 'n'
Duplicating IDs is a really bad idea (I've always been surprised that this is even allowed, but it is).
That leads to the question "why are you using an ID"? If you are going to replicate it like this, then either make the IDs unique or replace them with a class. (The class option is much better).
Even better, like #Cameron suggested, use a click binding to a function inside your KO ViewModel instead. This will also have the added benefit of automatically passing the bound data item (the current instance of the foreach loop) to the function.
<div id = "foo">
<!-- ko foreach: tweets -->
<input id = "queuedTweet" data-bind="value: tweet"/>
<a class="btn btn-primary" click="$parent.queueSubmit" >
Send
</a>
<a id="removeFromQueue" data-bind="click: $parent.removeTweet, value:index" class="btn btn-danger">
Delete
</a>
</br>
<!-- /ko -->
And then in your ViewModel:
self.queueSubmit = function(item) { // <== current tweet is passed in automatically
$.ajax({
type: 'POST',
url: nodeApiUrl + 'twitter/update_status/',
contentType: 'application/json',
data: JSON.stringify({'status': item.tweet }), // <== use the item, not jQuery
dataType: 'json',
error: $.osf.handleJSONError
});
};
You can learn more about the click binding at the KO website

You can't use IDs in this way. You should register a click binding.

Related

How can I change icon on AJax response?

I have an anchor tag with an icon inside that I want to change it on click.
So, using Ajax I tried as below:
HTML
<a id="#pl-esong234" class="social-button-song" title="Add in playlist" onclick="addInPlaylistSongs(234, 1)">
<i class="ion-plus all-btn-icon"></i>
</a>
JS
function addInPlaylistSongs(track, id) {
$.ajax({
type: "POST",
url: baseUrl+"/requests/manage_playlists.php",
data: "id="+track+"&playlist="+id+"&type=4&token_id="+token_id,
cache: false,
success: function(html) {
$('#pl-esong'+track).append('<i class="ion-checkmark all-btn-icon"></i>');
}
});
}
I tried also:
//2nd try
$('#pl-esong'+track+' i').after('<i class="ion-checkmark all-btn-icon"></i>').remove();
//3rd try
$('#pl-esong'+track+' i').after(html).remove();
//4th try
$('#pl-esong'+track+' i').replaceWith(html);
I tried to console log on success and everything is correct, the html response is <i class="ion-checkmark all-btn-icon"></i> and the select item is #pl-esong234.
Why I'm not able to change the icon inside my anchor element?
Remove the hash from the id. It is using by the selectors to find the matched tag.
# - used to find by id.
. - user to find by class
<a id="pl-esong234" class="social-button-song" title="Add in playlist" onclick="addInPlaylistSongs(234, 1)">
<i class="ion-plus all-btn-icon"></i>
</a>
and call html().
$('#pl-esong'+track+' i').html('<i class="ion-checkmark all-btn-icon"></i>');

Sending a Javascript array to controller ASP.NET MVC

I'm trying to store user input in a javascript array and send it to controller via ajax call. But all I get in controller's parameter is null.
Here's the code:
<table class="table-condensed table-bordered table-striped table-responsive">
#foreach (var project in projects)
{
<tr>
#foreach (var parameter in parameters)
{
<td>
<input type="text" class="form-control remar" id=#i />
</td>
i++;
}
</tr>
}
<tr>
<td colspan=#(parameters.Count() + 1)>
<button class="btn btn-primary pull-right" onclick="insert()">Submit Remarks</button>
</td>
</tr>
</table>
<script>
function insert() {
var remarks = [];
jQuery(".remark").each(function () {
remarks.push(jQuery(this).val());
});
$.ajax({
type: "POST",
url: "#Url.Action("AddRemarksToEvaluationSheet", "Teacher")",
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
}
</script>
Controller:
public ActionResult AddRemarksToEvaluationSheet(string[] function_param)
{
return View();
}
Any help?
P.S. the above code is edited. It worked!
You've got lots going on here...
First, don't give your input boxes ids of numbers - in this scenario, it doesn't look like you even use the value...But if you need it, put the value into a data element:
<input type="text" class="form-control remark" data-remark-id="#i" />
When retrieving the values, you need to get the value, not the textbox itself:
var remarks = [];
jQuery(".remark").each(function() {
remarks.push(jQuery(this).val());
});
When doing anything weird with parameters, like arrays or complex objects, if you use JSON instead of the default of URL-encoded, it will make things nicer.
You should also avoid absolute paths, and use Url.Action instead, so that it'll work regardless of where your app lives relative to the domain.
$.ajax({
type: "POST",
url: "#Url.Action("AddRemarksToEvaluationSheet", "Teacher")",
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
And you can accept an array of strings, rather than of objects:
[HttpPost]
public ActionResult AddRemarksToEvaluationSheet(string[] function_param)
{
}
I have a feeling that you aren't getting the remarks in the array in the first place.
If you aren't already, use a browser that allows you to debug the js. If you use Chrome, right-click -> inpsect element (or F12). Go to the 'Sources' tab, go to your js file and put a break point to see what the remarks array looks like.
Regarding the code:
You do not seem to need id attributes on the inputs. Let alone numerical ids.
To populate the remarks array, get all dom elements having the class you placed on all inputs. For each one, push the value in the array.
var remarks = [];
$(".form-control").each(function() {
remarks.push($(this).val());
});
You can add extra code to only add the ones with value.
var remarks = [];
$(".form-control").each(function() {
if($(this).val().length){
remarks.push($(this).val());
}
});
The ajax call:
$.ajax({
type: "POST",
url: addRemarksUrl,
data: JSON.stringify({ function_param: remarks }),
contentType: "application/json; charset=utf-8;"
});
Where addRemarksUrl can be a global variable declared in the html.
There are other ways of getting the url. Have a look here:
How to send razor created Url to js file
This user offers 3 possible solutions:
global js variable
custom "data-" attribute
hidden input

angularjs ng-show binding doesn't work with jQuery

I have some ng-repeat
<tr ng-repeat="Picture in Pictures">
<td>{{Picture.Id}}</td>
<td>
<div ng-hide="Picture.editorEnabled">
{{Picture.Name}}
</div>
<div ng-show="Picture.editorEnabled">
<input ng-model="Picture.editName" class="form-control" ng-show="Picture.editorEnabled">
</div>
</td>
<td>
<button type="button" name="editButton" class="btn btn-warning" ng-hide="Picture.editorEnabled"
ng-click="enableEditor(Picture)">Edit
</button>
<div ng-show="Picture.editorEnabled">
<button type="button" name="saveEditButton" class="btn btn-success"
ng-click="saveEditor(editName,editImageUrl,Picture)">Save
</button>
<button type="button" name="cancelEditButton" class="btn btn-warning" ng-click="disableEditor(Picture)">
Cancel
</button>
</div>
</td>
</tr>
and here is my controller code:
$scope.enableEditor = function(picture) {
picture.editorEnabled = true;
picture.editName = picture.Name;
picture.editImageUrl = picture.ImageUrl;
};
$scope.disableEditor = function(picture) {
picture.editorEnabled = false;
picture.editName = null;
picture.editImageUrl = null;
};
$scope.saveEditor = function(name, url, picture) {
$.ajax({
url: 'admin/pictures/edit/' + picture.Id,
type: 'POST',
data: {
ImageUrl: url,
Name: name
},
success: function() {
picture.Name = picture.editName;
picture.ImageUrl = picture.editImageUrl;
$scope.disableEditor(picture);
}
});
};
when I click 'Edit' appears editing fields and Save,Cancel buttons.
when I click 'Cancel' it dissapears.
when I click 'Save' fields saves to DB, method disableEditor executes on same object, I checked Id property, and in debugger it showes that editorEnabled is false, but in my browser still exist Save,Cancel buttons and fields for editing. I click again on 'Save' and it dissapears.
You should try using $http instead of $.ajax. jQuery's $.ajax executes outside of Angular's scope. Any code in a callback would not be reflected on your scope because this occurs outside of Angular's digest cycle. Try the following instead:
var url = 'admin/pictures/edit/' + picture.Id;
var data = $httpParamSerializerJQLike({
ImageUrl: url,
Name: name
});
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
$http.post(url, data, config).then(function() {
picture.Name = picture.editName;
picture.ImageUrl = picture.editImageUrl;
$scope.disableEditor(picture);
});
For this to work, you'll need to inject the $http and $httpParamSerializerJQLike services into your controller. I'm assuming you need to submit this as form, since that's how $.ajax works by default. This requires Angular 1.4+.
Note, while it is possible to use $.ajax and then wrap your success code in a $timeout or $scope.$apply(), you're better off using Angular's own $http.
This does not work because everything that you do in $scope.saveEditor happens outside of angular (I assume $ is jQuery). This is simply not the angular way.
You should have a deeper look into https://docs.angularjs.org/api/ng/service/$http the angular HTTP service, and use that instead.
You need to use $apply every time you use something that is not "angular way", like Anid has already mentioned.
For example if you use jQuery's http instead of angular's $http, you will have to add $scope.$apply.

Passing parameters to on click jquery event from dynamic list of anchor tags

I'm working on a list of elements in my asp.net mvc project. Each element is part of a ul, and the list elements are generated based on a list in my model.
I'm trying to add a delete button to each of these elements, but I'm struggelig a bit with how to make these elements unique, for jquery to pass the correct parameters to my action later on.
Each element has its own guid, but I can't figure out how to pass these along to the .on('click') jquery handler.
Here's the relevant part of my razor view:
<ul class="panel-tasks ui-sortable">
#foreach (RunModel run in Model.PlannedRuns)
{
<li>
<label>
<i class="fa fa-edit"></i>
<!--<i class="fa fa-ellipsis-v icon-dragtask"></i>-->
<span class="task-description">#run.Name</span>
<span class="sl-task-details">#run.RunTask</span>
<span class="sl-task-unit">#run.ConveyanceId</span>
<span class="sl-task-location">#run.Operation.WellContract.Location, #run.Operation.WellContract.Name</span>
</label>
<div class="options">
</i>
</div>
</li>
}
</ul>
And here's my javascript:
<script type="text/javascript">
$("#del").on("click", function (runId) {
$.ajax({
url: "#Url.Action("DeleteRun", "Planning")",
type: "GET",
dataType: "json",
data: { runId: runId },
error: function (msg) {
// Error handling
},
success: function (msg) {
// Success handling
}
});
});
</script>
I do realize I could add onClick to the anchor tag passing along the id as a parameter there, but I was hoping using jquery would do the trick, like mentioned above. Also, is there a recommended approach for doing tasks like this when several html elements use the same method?
You can use a data-* parameter on the delete button specific to that instance which you can then retrieve on click. You also need to make the delete button use a class attribute, otherwise they will be duplicated in the loop. Try this:
<div class="options">
<i class="fa fa-trash-o"></i>
</div>
$(".del").on("click", function (e) {
e.preventDefault();
var runid = $(this).data('runid');
$.ajax({
url: "#Url.Action("DeleteRun", "Planning")",
type: "GET",
dataType: "json",
data: { runId: runId },
error: function (msg) {
// Error handling
},
success: function (msg) {
// Success handling
}
});
});
The answers using data- attributes are elegant and do the job. However, I would like to propose a (slightly) different approach:
#foreach (RunModel run in Model.PlannedRuns)
{
<li id="#run.Id">
...........
</li>
}
Inside the a elements, set your del id as class.
$(".del").on("click", function () {
var runid = $(this).parent().id;
//..............
});
The advantages of this solution:
Your li elements have an id which can be used by other JavaScript functions as well
No need to play around with attributes (be careful as Firefox is very picky with data- attributes)
Additionally, your delete a elements won't have duplicate ids.
You can access the item clicked with $(this) in a click event (and most events).
$(".del").on("click", function () {
var runId = $(this).data("runId");
$.ajax({
url: "#Url.Action("DeleteRun", "Planning")",
type: "GET",
dataType: "json",
data: { runId: runId },
error: function (msg) {
// Error handling
},
success: function (msg) {
// Success handling
}
});
});
and insert the id as data-runId= in the HTML:
<i class="fa fa-trash-o"></i>
As the delete button is a boookmark only (#) the only effect it may have is to move the page to the top. To stop that add an event parameter and call preventdefault() on it. (or simply return false from the event handler).
$(".del").on("click", function (e) {
e.preventDefault();
And as Rory McCrossan points out, you should not have duplicate ids on your delete buttons (use a class). Dupe ids will actually work on most browsers, but are considered a bad thing :)

Setting Html On Ajax Success

I have html that looks like this with several items
<div class="item">
<p class="price">$388.00</p>
<p class="part_number">VM2327T23A00T</p>
<p class="real_price"></p>
</div>
<div class="item">
<p class="price">$88.00</p>
<p class="part_number">AA327T23A00T</p>
<p class="real_price"></p>
</div>
<div class="item">
<p class="price">$38.00</p>
<p class="part_number">AA327T23A00T</p>
<p class="real_price"></p>
</div>
etc..
I am trying to iterate through each item and set its "real_price" to a value here is my code:
jQuery('.part_number').each(function () {
parts.push(SearchSpring.jQuery(this).text());
SearchSpring.jQuery.ajax(
{
type: "POST",
url: "RealTimePricing.aspx/TestInteraction",
data: "{param1:'brit', param2: 'nick'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
cache: false,
success: function (msg) {
jQuery("#real_price").html(msg.d);
}
}
);
});
}
I am getting the correct "msg" on success (I can see this in firebug) but it never sets the html, also it only iterates through the each 1 time..
I am not sure why this isnt setting the html
jQuery("#real_price").html(msg.d);
And why is my code not looping through all the ".part_number" tags in the html?
class="real_price" implies .real_price, but you have to make sure you're updating the correct element as well (there are several elements with that class). I suggest using .next as the element to update is next to each .part_number element:
jQuery(".part_number").each(function() {
var $elem = jQuery(this).next(".real_price");
and then:
$elem.html(msg.d);
Use a . symbol instead of #, which is reserved for querying by id. Also, you should do this in a way that accesses individual prices instead otherwise it will update all elements as stated below.
You are trying to update #real_price, which does not exists in your html. You can use the following:
jQuery(this).parent(".item").find(".real_price").html( msg.d ); which updates the other child .real_price of the .item

Categories