I have followed a tutorial that I found at http://www.dotnetcurry.com/aspnet-mvc/933/knockoutjs-aspnet-mvc-tutorial-beginner. The tutorial is great and covers add and update however there are no click handlers included for delete or cancelling the update.
I tried to follow the same pattern the author provided for saving data and I created a function to delete, however this does not work.
function deleteData(currentData) {
var postUrl = "";
var submitData = {
concerns_id: currentData.concerns_id(),
concerns_description: currentData.concerns_description(),
};
if (currentData.concerns_id > 0) {
postUrl = "/concerns/delete"
}
$.ajax({
type: "POST",
contentType: "application/json",
url: postUrl,
data: JSON.stringify(submitData)
}).done(function (id) {
currentData.concerns_id(id);
}).error(function (ex) {
alert("ERROR Deleting");
})
};
This is the table:
<table class="table">
<tr>
<th>concerns_id</th>
<th>concerns_description</th>
<th></th>
</tr>
<tbody data-bind="foreach: ConcernCollection">
<tr data-bind="template: { name: Mode, data: $data }"></tr>
</tbody>
</table>
The Templates:
<script type="text/html" id="display">
<td data-bind="text: concerns_id"></td>
<td data-bind="text: concerns_description"></td>
<td>
<button class="btn btn-success kout-edit">Edit</button>
<button class="btn btn-danger kout-delete">Delete</button>
</td>
</script>
<script type="text/html" id="edit">
<td><input type="text" data-bind="value: concerns_id " /></td>
<td><input type="text" data-bind="value: concerns_description" /></td>
<td>
<button class="btn btn-success kout-update">Update</button>
<button class="btn btn-danger kout-cancel">Cancel</button>
</td>
</script>
Full JS without the Delete Function that I tied to add:
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/concerns/GetConcerns",
}).done(function (data) {
$(data).each(function (index, element) {
var mappedItem =
{
concerns_id: ko.observable(element.concerns_id),
concerns_description: ko.observable(element.concerns_description),
Mode: ko.observable("display")
};
viewModel.ConcernCollection.push(mappedItem);
});
ko.applyBindings(viewModel);
}).error(function (ex) {
alert("Error");
});
$(document).on("click", ".kout-edit", null, function (ev) {
var current = ko.dataFor(this);
current.Mode("edit");
});
$(document).on("click", ".kout-update", null, function (ev) {
var current = ko.dataFor(this);
saveData(current);
current.Mode("display");
});
$(document).on("click", "#create", null, function (ev) {
var current = {
concerns_id: ko.observable(0),
concerns_description: ko.observable(),
Mode: ko.observable("edit")
}
viewModel.ConcernCollection.push(current);
});
function saveData(currentData) {
var postUrl = "";
var submitData = {
concerns_id: currentData.concerns_id(),
concerns_description: currentData.concerns_description(),
};
if (currentData.concerns_id && currentData.concerns_id() > 0) {
postUrl = "/concerns/Edit"
}
else {
postUrl = "/concerns/Create"
}
$.ajax({
type: "POST",
contentType: "application/json",
url: postUrl,
data: JSON.stringify(submitData)
}).done(function (id) {
currentData.concerns_id(id);
}).error(function (ex) {
alert("ERROR Saving");
})
}
});
Any help or guidance would be apprenticed this is my first time working with Knockout.js
Thanks,
I'm not gonna lie, your code is a little hard to follow. I really don't think you're getting the full knockout experience. I put together a tiny little demo to show you just how you can use knockout to add/remove items from a list and display them.
Knockout should be used for data-binding. You should honestly never need to use jQuery to attach listeners by class. That is how your code becomes spaghetti.
While your question doesn't ask it, I strongly recommend visiting http://learn.knockoutjs.com/ before continuing your tutorial much further.
I hope this helps!
function ViewModel() {
var self = this;
self.Items = ko.observableArray();
self.DeleteRow = function(row) {
// Your ajax call here
self.Items.remove(row);
}
self.AddRow = function() {
self.Items.push("Added Item at " + new Date());
}
self.LoadFakeData = function() {
// Put ajax calls here
for (i = 0; i < 10; i++) {
self.Items.push("Item " + i);
}
}
self.Load = function() {
self.LoadFakeData();
}
self.Load();
}
ko.applyBindings(new ViewModel())
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: Items">
<span data-bind="text: $data"></span>
<span data-bind="click: $parent.DeleteRow">X</span>
<br>
</div>
<hr>
<span data-bind="click: AddRow">Add Row</span>
Related
I have a table that i want to be able to update the status of each line that checkbox is on
(see attached screenshot)
The checkbox propery in the Model is Not Mapped to the database ([NotMapped])
Html:
<div class="row">
<div class="col-12 text-right">
<button class="btn btn-primary" onclick="ApproveStatus()">Approve Checked Lines</button>
</div>
</div>
javaScript:
#section Scripts{
<script type="text/javascript">
function ApproveStatus() {
var pdata = new FormData();
swal({
title: "Are you sure?",
text: "Once Updated, you will not be able to Undo this",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
$.ajax({
url: "PaymentHistory/ApproveStatus",
type: "POST",
data: pdata,
processData: false,
contentType: false,
success: function (data) {
swal("Success!", {
icon: "success",
});
}
});
setTimeout(function () {
location.reload()
}, 100);
} else {
swal("Nothing Changed!");
}
});
}
</script>
}
And in the Controller i have the function (haven't written the logic yet)
[HttpPost]
public IActionResult ApproveStatus()
{
}
table in html:
<table id="tblData" class="table table-striped table-bordered" style="width:100%">
<thead class="thead-dark">
<tr class="table-info">
<th>Address</th>
<th>Payment Type</th>
<th>Amount</th>
<th>Payment Date</th>
<th>Status</th>
<th></th>
</thead>
#foreach (PaymentHistory paymentHistory in Model)
{
<tr>
<td>#ViewBag.getPaymentAddress(paymentHistory.SentFromAddressId).ToString()</td> <td>#ViewBag.getPaymentType(paymentHistory.SentFromAddressId).ToString()</td>
<td>#paymentHistory.Amount$</td>
<td>#paymentHistory.PayDate</td>
<td>#paymentHistory.Status</td>
#if (paymentHistory.Status != "Approved")
{
<td>
<div class="text-center">
<input type="checkbox" asp-for="#paymentHistory.isChecked"/>
</div>
</td>
}
else
{
<td></td>
}
</tr>
}
</table>
My only issue is that i want to pass the Object from the View (that contains the lines and status of the checkbox) to the function in the controller as a parameter,
Any ideas how can i do this?
Thank you
i want to pass the Object from the View (that contains the lines and status of the checkbox) to the function in the controller as a parameter, Any ideas how can i do this?
To achieve your requirement, you can try to add a hidden field for SentFromAddressId field, like below.
<td>
<div class="text-center">
<input type="hidden" asp-for="#paymentHistory.SentFromAddressId" />
<input type="checkbox" asp-for="#paymentHistory.isChecked" />
</div>
</td>
then you can get the sentFromAddressId of each checked row and populate it in form data object.
var pdata = new FormData();
$("input[name='paymentHistory.isChecked']:checked").each(function (index, el) {
var sentFromAddressId = $(this).siblings("input[type='hidden']").val();
pdata.append("Ids", sentFromAddressId);
})
and post the data to action method with following code snippet.
$.ajax({
type: 'POST',
url: '/PaymentHistory/ApproveStatus',
data: pdata,
processData: false,
contentType: false,
datatype: 'json',
success: function (res) {
//...
}
});
ApproveStatus action method
public IActionResult ApproveStatus(int[] Ids)
{
//code logic here
//update corresponding record based on id within Ids
Get all checked checkboxes id in an array, use that array to update table
I would like to add the values of 'Cost' based on checkbox checked event on knock out.
<tbody data-bind="foreach: $root.searchedResults">
<!-- ko if: Id-->
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Cost"></td>
<td><input type="checkbox" data-bind="checked: $root.searchedResults().length > 0, click: $root.Hospital_click"></td>
</tr>
<!-- /ko -->
</tbody>
And the sum of cost should be displayed in
<div data-bind="text: ko.toJSON(SumofItems)"></div>
The click event is not updating my sum based selection. Can someone help?
self.Hospital_click = function () {
//Suggest
}
My View model is:
var SearchModel = function () {
var self = this;
self.searchFromDate = ko.observable('01/01/2014');
self.searchToDate = ko.observable('01/01/2016');
self.searchedResults = ko.observableArray([]);
self.search = function () {
var model = {
BeginDate: self.searchFromDate(),
EndDate: self.searchToDate()
};
$.ajax({
url: '#Url.Action("SearchSpend", "Analysis", new { Area = "Portal" })',
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: "json",
data: ko.toJSON(model),
success: function (response) {
self.searchedResults(response);
}
});
};
You could add a new observableArray with the id's of the selected results (selectedResults). The viewmodel could be:
function Result(id,cost) {
this.Id = id;
this.Cost = cost;
}
function viewModel() {
var self = this;
self.searchedResults = ko.observableArray([]);
self.selectedResults = ko.observableArray([])
self.totalCost = ko.computed(function() {
let total = 0;
for(let p = 0; p < self.searchedResults().length; ++p)
{
if (self.selectedResults.indexOf(self.searchedResults()[p].Id)>=0){
total += self.searchedResults()[p].Cost;
}
}
return total;
});
};
totalCost will return the sum of the Cost field for all selected results.
And the view could be something like this:
<table data-bind="foreach: searchedResults">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Cost"></td>
<td><input type="checkbox" value="" data-bind="checkedValue: Id, checked: $parent.selectedResults"></td>
</tr>
</table>
<div data-bind="text: totalCost"></div>
totalCost is a computed value that returns de sum of the Cost for only the selected results.
Here is a fiddle.
In the fiddle, the data comes from the array listOfResults. In your case, this comes from the success function (Ajax request); in this function you also needs to clean the selectedResults.
I am new in knockout and asp.net mvc both.
I am trying to Insert update delete data in database with knockout. My knockout model is
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.CityId = ko.observable();
self.CityName = ko.observable();
self.selectedCity = ko.observable();
// self.City = ko.observable();
selectCity = function (item) {
self.selectedCity(item);
}
//load
loadCitys = function () {
$.getJSON("/Admin/GetCitys", {}, function (result) {
var mappedCitys = ko.utils.arrayMap(result.Data, function (item) {
return new City(item);
});
self.Citys([]);
self.Citys.push.apply(self.Citys, mappedCitys);
});
}
//edit
EditCity = function (item) {
//what need to do here
// is it possible to fill the hidden fild and the text box ??
}
//save
SaveCity = function (item) {
City = new City(item);
$.ajax({
type: "POST",
url: "/Admin/SaveCity",
data: ko.toJSON({ City: City }),
contentType: "application/json",
success: function (result) {
if (result.Edit) {
City.CityId = result.Success;
City.CityName = item.CityName;
self.Citys.push(City);
toastr.success('City Information Save Successfully', 'Success');
}
else if (result.Edit == false) {
toastr.success('City Information Update Successfully', 'Success');
}
else {
toastr.error('There is an error please try again later', 'Errror');
}
}
});
}
//delete
DeleteCity = function (City) {
$.ajax("/Admin/DeleteCity", {
data: ko.toJSON({ CityId: City.CityId }),
type: "POST", contentType: "application/json",
success: function (result) {
if (result.Success) {
self.Citys.remove(City);
toastr.success('City Remove Successfully', 'Success');
}
else {
alert("Error..");
}
}
});
}
}
(function () {
ko.applyBindings(new CityViewModel, document.getElementById("Citys"));
loadCitys();
});
And my Html codes are
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: $root.Citys">
<tr data-bind="click: selectCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name…">
<button type="submit" data-bind="click: SaveCity" class="btn">Submit</button>
</fieldset>
With this codes I can get data form database display them successfully in my view file,
I delete the data from database, and I also can Insert data to database but here is a problem I can save data only 1st time when I change the textbox value (without page refresh) and try to save city information then it say (in Firebug on my javascript code):
TypeError: City is not a constructor
City = new City(item);
My question is what have I done wrong in this codes, and I am trying to fill the textbox and the hidden field when edit button click, how can I do this?
Thanks in advance.
There are a number of faults with your javascript, including:
The methods on your viewmodel, such as SaveCity, DeleteCity, EditCity are all being defined without the 'this/self' prefixes, therefore they are being added to the global namespace.
In your SaveCity method, your are assigning a new instance of the City class to a variable called 'City', therefore destroying the City class. It will work the first time, but any other attempts to create an instance of a City will yield an exception.
Anyway, this should be a working version of your script and HTML without the ajax stuff. You will need to adapt that yourself. I have also created a working JsFiddle here..
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.SelectedCity = ko.observable();
self.EditingCity = ko.observable(new City({CityId: null, CityName: ''}));
self.EditCity = function(city){
self.EditingCity(new City(ko.toJSON(city)));
};
//load
self.loadCitys = function () {
self.Citys().push(new City({CityId: '1245', CityName: 'Los Angeles'}));
self.Citys().push(new City({CityId: '45678', CityName: 'San Diego'}));
};
//save
self.SaveCity = function () {
var city = self.EditingCity();
if(city.CityId()){
var cityIndex;
for(var i = 0; i < self.Citys().length; i++) {
if(self.Citys()[i].CityId() === city.CityId()) {
cityIndex = i;
break;
}
}
self.Citys[cityIndex] = city;
}
else{
city.CityId(Math.floor((Math.random()*1000000)+1));
self.Citys.push(city);
}
self.EditingCity(new City({CityId: null, CityName: ''}));
}
//delete
self.DeleteCity = function (city) {
self.Citys.remove(city);
};
}
var viewModel = new CityViewModel();
viewModel.loadCitys();
ko.applyBindings(viewModel, document.getElementById("Citys"));
HTML
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Citys">
<tr data-bind="click: $root.SelectedCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: $root.EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: $root.DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset data-bind='with:EditingCity'>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name" />
<button type="submit" data-bind="click: $root.SaveCity" class="btn">Submit</button>
</fieldset>
script
<script type="text/javascript">
$(document).ready(function () {
$('musteri_sno').change(function () {
$('form_sayac_secimi').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
}
});
}
return false;
});
});
});
</script>
html
#using (Html.BeginForm("SayacSecimiPartial", "SayacOkumalari", FormMethod.Post, new { id = "form_sayac_secimi" }))
{
<table>
<tr>
<td>
#Html.DropDownList("musteri_sno", (SelectList)ViewBag.musteri_id, "--Müşteri Seçiniz--", new { id = "musteri_sno" })
</td>
<td>
#Html.DropDownList("sayac_no", Enumerable.Empty<SelectListItem>(), "-- Sayaç Seçiniz --", new { id = "sayac_no" })
</td>
<td>
<input type="submit" value="Uygula" />
#Html.Hidden("sno", new { sno = ViewData["sno"] })
</td>
</tr>
</table>
}
I want to fill second dropdown with values that is returned from first one.How can I do this?
Thanks.
In the success callback of your ajax call, build the option tag filled with the values you are returned and then append it to the select tag named "sayac_no".
success: function(result) {
var opt = '';
for (var i = 0; i < result; i++) {
opt += '<option value="' + result[i].value + '">' + result[i].name + '</option>';
}
$('select[name=sayac_no]').html(opt);
}
I suppose the result object is a list of objects with two properties, name and value.
Modify it accordingly to your needs and improve it because this is just a very basic version.
I am using asp.net MVC and I am having an issue posting a form using jquery.
It is not posting to the url I am telling it to.
If I use firebug, it shows the post happening but it is posting to the index of the controller everytime. I cannot figure out why. I have verified the url of the action I am trying to post but I can't figure out why it is always posting to the index of the controller....Note: the view in which the form is found IS the index view. so Basically it is posting to it's own action rather than the one in the url i am telling it to. Any help would be great. thanks!
here is my form
<form action='' id="descriptionForm">
<%=Html.Hidden("claimNumber", ViewData["claimNumber"])%>
<%=Html.Hidden("partNumber", ViewData["partNumber"])%>
<%=Html.Hidden("qty", ViewData["qty"])%>
<table>
<tr>
<td style="text-align: right">
Category:
</td>
<td>
<%=Html.DropDownList("problemCategory", (IEnumerable<SelectListItem>)ViewData["problemSelect"], "-Select-")%>
</td>
</tr>
<tr>
<td style="text-align: right">
Details:
</td>
<td>
<select id="problemDetails">
</select>
</td>
</tr>
<tr>
<td style="text-align: right">
Dealer Notes:
</td>
<td>
<%=Html.TextArea("dealerNotes", "", 3, 40, null)%>
</td>
</tr>
</table>
<div style="position: absolute; bottom: 8px; right: 8px">
<input type="button" id="itemDetailsCancel" value="Cancel" />
<input type="submit" id="itemDetailsSubmit" value="Save" />
</div>
</form>
<a href='<%=ResolveUrl("~/Warranty/WarrantyClaims/CompleteAddLineItemToClaim/") %>'
id="CompleteLineItemUrl"></a>
Here is my Javascript
$("#descriptionForm").submit(function () {
var completeurl = $("#CompleteLineItemUrl").attr('href');
var data = $(this).serialize();
$.post({
type:'POST',
url: completeurl,
data: data,
success: function (result) {
alert("done");
}
});
return false
});
and just for good measure here is the controller action I am trying to post to(though it doesn't do much yet)
[HttpPost]
public ActionResult CompleteAddLineItemToClaim(string claimNumber, string partNumber, string qty, string problemCategory, string problemDetails, string dealerNotes)
{
var result = new { result = "done" };
return Json(result, JsonRequestBehavior.AllowGet);
}
Update:
updated javascript
$(function(){
$('#descriptionForm').submit(function () {
var completeurl = $('#CompleteLineItemUrl').attr('href');
var data = $(this).serialize();
$.ajax({
type: 'POST',
url: completeurl,
data: data,
success: function (result) {
alert('done');
}
});
return false;
});
});
Is the form itself loaded by an ajax call?
If so you need to use the live() function of jquery.
Make sure you have wrapped your javascript in a document.ready before subscribing for any events. Also you have a missing ; when returning false at the end of your method.
But your real problem is that you want to use $.ajax instead of $.post. So what actually happens is that you are getting a javascript error because of wrongly using the $.post function and the .submit handler never has time to return false and cancel the default submission of the form and the browser happily proceeds into POSTing to the action of the form (which is empty and default to the action that rendered this form).
So to sum up:
$(function() {
$('#descriptionForm').submit(function () {
var completeurl = $('#CompleteLineItemUrl').attr('href');
var data = $(this).serialize();
$.ajax({
type: 'POST',
url: completeurl,
data: data,
success: function (result) {
alert('done');
}
});
return false;
});
});
Or if you wanted to use $.post:
$(function() {
$('#descriptionForm').submit(function () {
var completeurl = $('#CompleteLineItemUrl').attr('href');
var data = $(this).serialize();
$.post(completeurl, data, function (result) {
alert('done');
});
return false;
});
});
Also instead of generating links à la classic WebForms way:
In ASP.NET MVC you use HTML helpers in order to ensure that link urls are conform to your routes:
<%= Html.ActionLink(
"Link text",
"CompleteAddLineItemToClaim",
"WarrantyClaims",
new { area = "Warranty" },
new { id = "CompleteLineItemUrl" }
) %>