I'm building a multi-step form using knockoutjs in my asp.net mvc 5 application.
the problem is, during binding, the change event for my dropdown is firing. similar to this issue reported here.
I have tried everything suggested in the post above, but the event is still firing twice.
<div class="row">
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(model => model.Step1.Country, new { #class = "control-label" })
#Html.DropDownListFor(model => model.Step1.Country, Model.Step1.Countries, new { #class = "select-country", #style = "width: 100%;", data_bind = "value: Model.Step1.Country" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(model => model.Step1.State, new { #class = "control-label" })
#Html.DropDownListFor(model => model.Step1.State, Model.Step1.States, new { #style = "width: 100%;", data_bind = "value: Model.Step1.State" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
#Html.LabelFor(model => model.Step1.City, new { #class = "control-label" })
#Html.TextBoxFor(model => model.Step1.City, new { maxlength = "50", #class = "form-control", data_bind = "value: Model.Step1.City" })
</div>
</div>
</div>
Wizard Plugin:
(function ($) {
$.fn.Wizard = function (options) {
//Set the form of the current wizard
var $form = $("#" + options.formId);
var ViewModel = function (d, m) {
var self = this;
self.Model = ko.mapping.fromJS(d, m);
self.Model.GeneralErrors = ko.computed(function () {
return ko.utils.arrayFilter(self.Model.Errors(), function (item) {
return !!item.MemberName;
});
});
self.Model.Step1.Country.subscribe(function (value) {
alert(value);
console.log(value);
});
self.SelectionChanged = function (element) {
}
}
var vm = new ViewModel(options.model, options.mapping);
ko.applyBindings(vm, this[0]);
vm.UpdateOnChange = true;
return this;
};
}(jQuery));
<script type="text/javascript">
$(function () {
$("#WizardWrapper").Wizard({
formId: "WizardForm",
model: #(Html.Raw(Json.Encode(Model))),
url: "/DataCollection/#HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString()/",
mapping: {
create: function (options){
var m = ko.mapping.fromJS(options.data);
return m;
}
}
});
});
</script>
Does anyone know how I can fix this?
Thanks user3297291,
thanks, adding valueAllowUnset stopped the event firing when binding and fires when a selection is made.
However, just one problem,the event fires again during postback when save is click. the event is triggered again by
ko.mapping.fromJS(data, self.Model);
in the ajax post below.
Save Event:
self.Next = function (element) {
var validator = $(element).closest("form").validate();
if ($(element).closest("form").valid()) {
$.ajax({
url: options.url + action,
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: ko.toJSON(self.Model),
success: function (data) {
self.UpdateOnChange = false;
ko.mapping.fromJS(data, self.Model);
self.UpdateOnChange = true;
},
error: function (jqXHR, textStatus, errorThrown) {
}
});
} else {
validator.focusInvalid();
}
}
Button:
<input data-bind="event: { click: Next.bind($data, $element) }" type="submit" class="btn btn-default btn-prev btn-sky" value="Next" />
Update:
In order to fix the issue with the subscription triggering again after Country is updated, I used this solution with a few changes.
self.dirty = ko.observable(false);
self.selectedCountry = ko.observable();
self.dirtyCalculations = ko.computed(function () {
var value = self.Model.Step1.Country();
if (value == self.selectedCountry()) {
self.selectedCountry(null);
self.dirty(false);
} else {
self.selectedCountry(value);
self.dirty(true);
}
});
self.Model.Step1.Country.subscribe(function (value) {
if (value != undefined && self.dirty()) {
$.ajax({
url: options.url + "GetState",
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: ko.toJSON(self.Model),
traditional: true,
success: function (data) {
self.UpdateOnChange = false;
ko.mapping.fromJS(data, self.Model);
self.UpdateOnChange = true;
}
});
} else {
self.resetDirtyFlag();
}
});
Simple answer: You could (mis?)use the valueAllowUnset binding.
var VM = function(initial) {
this.selection = ko.observable(initial);
this.updateCount = ko.observable(0);
this.selection.subscribe(function() {
this.updateCount(this.updateCount() + 1);
}, this);
}
ko.applyBindings(new VM(1), document.querySelector(".example1"));
ko.applyBindings(new VM(2), document.querySelector(".example2"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="example1">
<h1>with valueAllowUnset</h1>
<select name="" id="" data-bind="value: selection, valueAllowUnset: true">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
<div>
Updates: <span data-bind="text: updateCount"></span>
</div>
</div>
<div class="example2">
<h1>without valueAllowUnset</h1>
<select name="" id="" data-bind="value: selection">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
<div>
Updates: <span data-bind="text: updateCount"></span>
</div>
</div>
Alternative solution: I'd suggest to use the options binding to render your options via knockout instead of MVC. It makes it a bit easier to debug client side. But this might be personal preference.
Related
My Validation is not firing on my popup its showing the popup just fine just not doing the validation
<div>
<form id="myForm">
#Html.HiddenFor(m => m.Id)
#Html.TextBoxFor(model => model.Name, new { #class = "form-control", #placeholder = "Name" })
<span asp-validation-for="#Model.Name" class="text-danger"></span>
#Html.TextBoxFor(model => model.Resitance, new { #class = "form-control", #placeholder = "Address" })
<span asp-validation-for="#Model.Resitance" class="text-danger"></span>
#Html.TextBoxFor(model => model.Passed, new { #class = "form-control", #placeholder = "Passed" })
<span asp-validation-for="#Model.Passed" class="text-danger"></span>
<select asp-for="CirtcutType"
asp-items="#Html.GetEnumSelectList(typeof(ElectricalSurvey.DAL.Models.CircuitModel.CirtcutTypes))"
class="form-control"></select>
<div class="modal-footer">
#if (Model.Id > 0) {<span>Update</span> } else {<span>Save</span>}
</div>
</form>
<div style="text-align:center;display:none" id="loaderDiv">
<img src="~/Content/InternetSlowdown_Day.gif" width="150" />
</div>
</div>
<script>
$(document).ready(function () {
$("#btnSubmit").click(function () {
$("#loaderDiv").show();
var myformdata = $("#myForm").serialize();
$.ajax({
type: "POST",
url: "/Electrician/SaveCircuit",
data: myformdata,
success: function () {
$("#loaderDiv").hide();
$("#MyEditUpateModal").modal("hide");
window.location.href = "/Electrician/Index";
}
})
})
})
_ValidationScriptsPartial.cshtml
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
To Note I am handing the dialog in html via following method.
<script>
var AddOrUpdateCircuit = function (id) {
var url = "/Electrician/AddOrUpdateCircut?id=" + id;
$("#myModalBodyDiv1").load(url, function () {
$("#MyEditUpateModal").modal("show");
})
}
This one will help you get started
<form id="myForm" class="needs-validation" novalidate>
and
$("#btnSubmit").click(function (e) {
e.preventDefault(); // do not execute actual submit.
var form = $("#myForm")[0];
form.classList.remove('was-validated');
if (form.checkValidity() === false) {
form.classList.add('was-validated');
return;
}
$.ajax({
//perform ajax
});
})
I'm developing an application with asp.net mvc. I am not doing a screen for preparing questions for surveys and answers to these questions. On the home screen is a table of questions. When I press 'add new question' button, I open a popup with jquery and add the question and answer options in this question ('popup is independent of the main screen, ie Layout = null'). Then, when the 'submit' button of this popup is pressed, I validate the form in the popup with javascrit in addOrEdit.cshtml. If the validation is successful, my goal is to submit the form submit event of asp.net mvc to the javascript function on the main page. I can't do this. Where am I making a mistake. What is the problem. I tried to explain it in an explanatory way. I also added screenshots and codes.
Index.cshtml
#{
ViewBag.Title = "Soru Listesi";
}
<h2>Add Question</h2>
<a class="btn btn-success" style="margin-bottom: 10px" onclick="PopupForm('#Url.Action("AddOrEdit","Question")')"><i class="fa fa-plus"></i> Add New Question</a>
//table heree
Index.cshtml sectionscript
#section Scripts{
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<script>
//datatable script hereee.....
function PopupForm(url) {
var formDiv = $('<div/>');
$.get(url)
.done(function (response) {
formDiv.html(response);
Popup = formDiv.dialog({
autoOpen: true,
resizable: true,
title: 'Soru Detay',
modal: true,
height: 'auto',
width: '700',
close: function () {
Popup.dialog('destroy').remove();
}
});
});
}
function SubmitForm(form) {
alert('gel babanaaa');
if ($(form).valid()) {
alert('validd');
$.ajax({
type: "POST",
url: form.action,
data: $(form).serialize(),
success: function (data) {
if (data.success) {
Popup.dialog('close');
dataTable.ajax.reload();
$.notify(data.message,
{
globalPosition: "top center",
className: "success",
showAnimation: "slideDown",
showDuration: 500,
gap: 1000
});
}
}
});
}
}
</script>
}
AddOrEdit.cshtml
#model MerinosSurvey.Models.Questions
#{
Layout = null;
}
#using (Html.BeginForm("AddOrEdit", "Question", FormMethod.Post, new { #class = "needs-validation", novalidate = "true", onsubmit = "return SubmitForm(this)", onreset = "return ResetForm(this)", id = "questionForm" }))
{
// other component heree
<div class="form-group row">
<input type="button" value="Submit" class="btn btn-primary" id="btnSubmit" />
<input type="reset" value="Reset" class="btn btn-secondary" />
</div>
}
AddOrEdit.cshtml scripts
<script>
//some scriptt for validationn...
$("#btnSubmit").click(function (event) {
var form = $("#questionForm");
if (form[0].checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.addClass('was-validated');
// Perform ajax submit here...
if ($(form).valid()) {
form[0].submitEvent();//MY PROBLEM!!!!!
}
});
</script>
I want to call SubmitForm event in asp.net mvc after button click and validation. And I used form[0].submitEvent(); SO I can't send a request via AJAX. but I doesn't work.
replaces your Index.cshtml with the code below
#{
ViewBag.Title = "Soru Listesi";
}
<h2>Add Question</h2>
<a class="btn btn-success" style="margin-bottom: 10px" onclick="PopupForm('#Url.Action("AddOrEdit","Question")')"><i class="fa fa-plus"></i> Add New Question</a>
<table id="questionTable" class="table table-striped table-bordered accent-blue" style="width: 100%">
<thead>
<tr>
<th>Soru No</th>
<th>Soru Adı</th>
<th>Oluşturma Tarihi</th>
<th>Güncelleme Tarihi</th>
<th>Detay/Güncelle/Sil</th>
</tr>
</thead>
</table>
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" />
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
#section Scripts{
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.validation/1.16.0/jquery.validate.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.1/jquery-ui.min.js"></script>
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<script>
var Popup, dataTable;
$(document).ready(function () {
dataTable = $("#questionTable").DataTable({
"ajax": {
"url": "/Question/GetData",
"type": "GET",
"datatype": "json"
},
"columnDefs": [
{ width: '10%', targets: 5 }
],
"scrollX": true,
"scrollY": "auto",
"columns": [
{ "data": "QuestionId" },
{ "data": "QuestionName" },
{
"data": "CreatedDate",
"render": function (data) { return getDateString(data); }
},
{
"data": "UpdatedDate",
"render": function (data) { return getDateString(data); }
},
{
"data": "QuestionId",
"render": function (data) {
return "<a class='btn btn-primary btn-sm' onclick=PopupForm('#Url.Action("AddOrEdit", "Question")/" +
data +
"')><i class='fa fa-pencil'></i> Güncelle</a><a class='btn btn-danger btn-sm' style='margin-left:5px' onclick=Delete(" +
data +
")><i class='fa fa-trash'></i> Sil</a>";
},
"orderable": false,
"searchable": false,
"width": "150px"
}
],
"language": {
"emptyTable":
"Soru bulunamadı, lütfen <b>Yeni Soru Oluştur</b> butonuna tıklayarak yeni anket oluşturunuz. "
}
});
});
function getDateString(date) {
var dateObj = new Date(parseInt(date.substr(6)));
let year = dateObj.getFullYear();
let month = (1 + dateObj.getMonth()).toString().padStart(2, '0');
let day = dateObj.getDate().toString().padStart(2, '0');
return day + '/' + month + '/' + year;
};
function PopupForm(url) {
var formDiv = $('<div/>');
$.get(url)
.done(function (response) {
formDiv.html(response);
Popup = formDiv.dialog({
autoOpen: true,
resizable: true,
title: 'Soru Detay',
modal: true,
height: 'auto',
width: '700',
close: function () {
Popup.dialog('destroy').remove();
}
});
});
}
function SubmitForm(form) {
alert('Submit Formm');
if (form[0].checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.addClass('was-validated');
if ($(form).valid()) {
alert('ben burdyaım');
}
}
function ResetForm(form) {
Popup.dialog('close');
return false;
}
function Delete(id) {
if (confirm('Bu soruyu silmek istediğinizden emin misiniz?')) {
$.ajax({
type: "POST",
url: '#Url.Action("Delete", "Question")/' + id,
success: function (data) {
if (data.success) {
dataTable.ajax.reload();
$.notify(data.message,
{
className: "success",
globalPosition: "top center",
title: "BAŞARILI"
})
}
}
});
}
}
</script>
}
replaces your AddOrEdit.cshtml with the code below
#model MerinosSurvey.Models.Questions
#{
Layout = null;
}
#using (Html.BeginForm("AddOrEdit", "Question", FormMethod.Post, new { #class = "needs-validation", novalidate = "true", onsubmit = "return SubmitForm(this)", onreset = "return ResetForm(this)", id = "questionForm" }))
{
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group row">
#Html.LabelFor(model => model.QuestionId, new { #class = "col-form-label col-md-3" })
<div class="col-md-9">
#Html.TextBoxFor(model => model.QuestionId, new { #readonly = "readonly", #class = "form-control" })
</div>
</div>
<div class="form-group row">
#Html.LabelFor(model => model.QuestionName, new { #class = "col-form-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.QuestionName, new { htmlAttributes = new { #class = "form-control", required = "true" } })
<div class="valid-feedback"><i class="fa fa-check">Süpersin</i></div>
<div class="invalid-feedback "><i class="fa fa-times"></i></div>
</div>
</div>
<div class="form-group row">
#Html.LabelFor(model => model.CreatedDate, new { #class = "form-control-label col-md-3"})
<div class="col-md-9">
#Html.EditorFor(model => model.CreatedDate, "{0:yyyy-MM-dd}", new { htmlAttributes = new { #class = "form-control", type = "date", #readonly = "readonly",required="false" } })
</div>
</div>
<div class="form-group row ">
<label class="col-sm-3 col-form-label">Options</label>
<div class="col-sm-9 input-group">
<input type="text" class="form-control" name="option[]" required placeholder="Seçenek giriniz" />
<div class="input-group-append">
<button type="button" class="btn btn-success addButton"><i class="fa fa-plus"></i></button>
</div>
</div>
</div>
<!-- The option field template containing an option field and a Remove button -->
<div class="form-group d-none row" id="optionTemplate">
<div class="offset-sm-3 col-sm-9 input-group">
<input class="form-control" type="text" name="option[]" required placeholder="Diğer seçenek giriniz." />
<div class="input-group-append">
<button type="button" class="btn btn-danger removeButton"><i class="fa fa-times"></i></button>
</div>
</div>
</div>
<div class="form-group row">
<input type="button" value="Submit" class="btn btn-primary" id="btnSubmit" />
<input type="reset" value="Reset" class="btn btn-secondary" />
</div>
}
<script>
$(document).ready(function () {
// The maximum number of options
var MAX_OPTIONS = 5;
$('#questionForm').on('click', '.addButton', function () {
var $template = $('#optionTemplate'),
$clone = $template
.clone()
.removeClass('d-none')
.removeAttr('id')
.insertBefore($template),
$option = $clone.find('[name="option[]"]');
// Add new field
$('#questionForm').bootstrapValidator('addField', $option);
})
// Remove button click handler
.on('click', '.removeButton', function () {
var $row = $(this).parents('.form-group'),
$option = $row.find('[name="option[]"]');
// Remove element containing the option
$row.remove();
// Remove field
$('#questionForm').bootstrapValidator('removeField', $option);
})
// Called after adding new field
.on('added.field.bv', function (e, data) {
// data.field --> The field name
// data.element --> The new field element
// data.options --> The new field options
if (data.field === 'option[]') {
if ($('#questionForm').find(':visible[name="option[]"]').length >= MAX_OPTIONS) {
$('#questionForm').find('.addButton').attr('disabled', 'disabled');
}
}
})
// Called after removing the field
.on('removed.field.bv', function (e, data) {
if (data.field === 'option[]') {
if ($('#questionForm').find(':visible[name="option[]"]').length < MAX_OPTIONS) {
$('#questionForm').find('.addButton').removeAttr('disabled');
}
}
});
});
$("#btnSubmit").click(function (event) {
var form = $("#questionForm");
if (form[0].checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.addClass('was-validated');
SubmitForm(form);
});
</script>
The data is updated to my database table when the modal form is submitted and closes successfully but what I need is without a page refresh data should be updated in cells of the table.
I am new to JQuery, If someone resolve this matter to me I would really appreciate it.
Here It is the Script which I have placed at the end of my partial view:
function btndbsave(obj) {
var ele = $(obj);
var id = ele.attr("data-model-id");
var itemlist = [];
var name = $("[name = 'NameTxtID']").val();
var phone = $("[name = 'PhoneTxtID']").val();
var email = $("[name ='EmailTxtID']").val();
var AjaxVM = { Id: id, Name: name, Phone: phone, Email: email };
itemlist.push(AjaxVM);
console.log(itemlist)
debugger;
$.ajax({
url: '/Home/Edit', //
dataType: "json",
data: JSON.stringify({ AjaxVM }),
type: "POST",
contentType: "application/json; charset=utf-8",
success: function () {
alert("success");
$('#newmodal').modal('hide');
$('#tbDetails>tbody>td').find("tr").html(AjaxVM);
//$('#tbDetails').find("tbody>tr").append(itemlist);
},
error: function (xhr) {
alert("error");
}
});
};
Partial View As a Modal:
<div class="modal-header">
<h5 class="modal-title">Edit Record</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(x => x.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(x => x.Name, "NameTxt", "NameTxtID", new { htmlAttributes = new { #class = "form-control col-md-6", #name = "Name[]" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(x => x.Phone, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(x => x.Phone, "PhoneTxt", "PhoneTxtID", new { htmlAttributes = new { #class = "form-control col-md-6", #name = "Phone[]" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(x => x.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(x => x.Email, "EmailTxt", "EmailTxtID", new { htmlAttributes = new { #class = "form-control col-md-6", #name = "Email[]" } })
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-primary btnSave" data-model-id="#Model.Id" onclick="btndbsave(this)" value="Save changes">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
Here it the table of View :
<table id="tbDetails" class="table table-bordered table-striped table-hover">
<thead>
<tr>
<td>Id</td>
<td>Name</td>
<td>Phone</td>
<td>Email</td>
<td>Options</td>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td width="100" class="idField">#item.Id</td>
<td>#item.Name</td>
<td>#item.Phone</td>
<td>#item.Email</td>
<td>
<a class="delete" data-model-id="#item.Id" onclick="Delete(this)"><img src="/UserImages/delete.png" /></a>
<a class="edit-record" data-model-id="#item.Id" onclick="Edit(this)" ><img src="/UserImages/pencil.png" /></a>
</td>
</tr>
}
</tbody>
</table>
Here it is the Controller:
[HttpPost]
public ActionResult Edit(Models.AjaxVM ajaxVM)
{
using (var db = new PracticeEntities())
{
var checkforid = db.AjaxTable.Where(x => x.Id == ajaxVM.Id).FirstOrDefault();
if (checkforid != null)
{
checkforid.Name = ajaxVM.Name;
checkforid.Email = ajaxVM.Email;
checkforid.Phone = ajaxVM.Phone;
db.SaveChanges();
}
else
{
ModelState.AddModelError("error", "Record has not been Updated");
}
return Json(ajaxVM);
}
}
Edit method in a Seperate JS file :
function Edit(obj) {
debugger;
var ele = $(obj);
var url = "/Home/Edit"; // the url to the controller
var id = ele.attr('data-model-id'); // the id that's given to each button in the list
$.get(url + '/' + id, function (data) {
$('#newmodal').find(".modal-content").html(data);
$('#newmodal').modal('show');
});
};
First, your use of EditorFor() makes no sense, and the usage should be just
#Html.EditorFor(x => x.Name, "new { htmlAttributes = new { #class = "form-control col-md-6" } })
Next you script to save the data should be
function btndbsave(obj) {
var ele = $(obj);
var id = ele.attr("data-model-id");
var name = $("#Name]").val();
var phone = $("#Phone").val();
var email = $("#Email").val();
var AjaxVM = { Id: id, Name: name, Phone: phone, Email: email };
$.ajax({
url: '#Url.Action("Edit", "Home")', // don't hard code your url's
dataType: "json",
data: AjaxVM,
type: "POST",
success: function (response) {
.... // see notes below
},
error: function (xhr) {
alert("error");
}
});
};
Note however that you modal should include a <form> and the #Html.ValidationMessageFor() code to give you client side validation along with a submit button so that your script becomes
$('form').submit(function(e) { // or give the form an id attribute and use that
e.preventDefault();
... // make ajax call
});
Next, you controller method need only return a value indicating success or otherwise (along with any error message if applicable), for example
return Json(new { success = true }); // if saved
return Json(new { success = false, message = "...." }); if not saved
Note that adding a ModelStateError makes no sense because you are not returning a view
and then in the ajax call back, you update the <td> elements is successful (or display error if not). In order to do that, include a global javascript variable to store the current row
and change the link that opens the modal to
<a class="edit-record" data-model-id="#item.Id" href="#" ><img src="/UserImages/pencil.png" /></a>
and the script to
var currentrow;
$('.edit-record').click(function() {
currentrow = $(this).closest('tr'); // assign the current row
... // load and display the modal
});
$.ajax({
....
success: function (response) {
if (response.success) {
// Update the current row
var cells = currentrow.find('td');
cells.eq(1).text(name);
cells.eq(2).text(phone);
cells.eq(3).text(email);
} else {
... // oops - display the message in response.message?
}
}
....
}
Note also that there is no need to make an ajax call to load the modal because you already have all the data in the view. Instead, include the html for editing a AjaxVM in the initial view, for example, using
#Html.Partial(""_Edit", new AjaxVM())
then update the values of the inputs when you display the modal
$('.edit-record').click(function() {
currentrow = $(this).closest('tr');
var cells = currentrow.find('td');
$('#Name').val(cells.eq(1).text());
$('#Phone').val(cells.eq(2).text());
$('#Email').val(cells.eq(3).text());
$('#newmodal').modal('show');
});
I'm using jQuery Ajax to load the data from the server and then bind it using Knockout JS, but the values do not display in DropDownList.
Here is the code:
View: Edit.cshtml
...
<div class="tab-content">
...
<div class="tab-pane" id="pnlLiquidation">
#{ Html.RenderPartial("~/Views/Liquidation/_Liquidation.cshtml", Model.LiquidationVM); }
</div>
</div>
...
#section Scripts {
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/knockout")
<script>
...
var companyID = #Html.Raw(ViewBag.CompanyID);
...
</script>
...
<script src="~/Scripts/LiquidationSystem/account-statement.js"></script>
...
}
View: _Liquidation.cshtml
...
<div class="tab-content">
...
<div class="tab-pane" id="pnlStage2Steps28_29">
#{ Html.RenderPartial("~/Views/Liquidation/_Stage2Steps28_29.cshtml", Model); }
</div>
...
</div>
View: _Stage2Steps28_29.cshtml
...
<div class="panel panel-primary">
<div class="panel-body" id="pnlAccountStatement">
#{ Html.RenderPartial("~/Views/AccountStatement/_AccountStatement.cshtml"); }
</div>
</div>
...
View: _AccountStatement.cshtml
<div class="row">
<div class="col-md-12">
Add New Statement of Accounts
<div class="form-horizontal" id="pnlAddEditAccountStatement">
<div data-bind="if: AccountStatement">
<h4>Update Statement of Account</h4>
...
</div>
<div data-bind="ifnot: AccountStatement()">
<h4>Add New Statement of Account</h4>
<div class="form-group">
<label class="control-label col-md-2">Type:</label>
<div class="col-md-10">
<select class="form-control"
data-bind="options: Types, optionsValue: 'Value', optionsText: 'Text', optionsCaption: '-- Please Select --', value: Type"></select>
</div>
</div>
<div class="form-group">
<input type="hidden" data-bind="value: $root.CompanyID" />
<label class="control-label col-md-2">Description:</label>
<div class="col-md-10">
<input type="text" placeholder="Enter Description" class="form-control"
data-bind="value: $root.Description" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">Amount:</label>
<div class="col-md-4">
<input type="text" placeholder="Enter Amount" class="form-control"
data-bind="value: $root.Amount" />
</div>
<label class="control-label col-md-2">Receipt Date:</label>
<div class="col-md-4">
<input type="text" placeholder="Enter Receipt Date" class="form-control fdatepicker" readonly="readonly"
data-bind="value: $root.ReceiptDate" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
Save
Reset
Cancel
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h4>
<i>
<u>List of Statement of Accounts</u>
</i>
</h4>
...
</div>
</div>
Javascript: account-statement.js
function AccountStatementViewModel(companyID) {
var self = this;
self.AccountStatementID = ko.observable();
self.CompanyID = ko.observable(companyID);
self.Description = ko.observable();
self.Amount = ko.observable();
self.ReceiptDate = ko.observable();
self.Type = ko.observable();
var AccountStatement = {
AccountStatementID: self.AccountStatementID,
CompanyID: self.CompanyID,
Description: self.Description,
Amount: self.Amount,
ReceiptDate: self.ReceiptDate,
Type: self.Type
}
self.AccountStatement = ko.observable();
self.AccountStatements = ko.observableArray();
$.ajax({
url: webroot + 'AccountStatement/GetAccountStatements',
contentType: 'application/json; charset=utf-8',
data: { id: self.CompanyID },
cache: false
}).done(function (data) {
self.AccountStatements(data);
});
var clearInput = function () { ... }
self.create = function () { ... }
self.reset = function () { ... }
self.edit = function (accountStatement) { ... }
self.update = function () { ... }
self.cancel = function () { ... }
self.delete = function (accountStatement) { ... }
}
var accountStatementVM = new AccountStatementViewModel(companyID);
ko.applyBindings(accountStatementVM, document.getElementById('pnlAccountStatement'));
var Types;
$(function () {
$('#pnlAddEditAccountStatement').hide();
$('#lnkAddAccountStatement').click(function (e) {
e.preventDefault();
$('#pnlAddEditAccountStatement').show('blind', 1000);
$(this).hide('blind', 1000);
});
$.ajax({
url: webroot + 'AccountStatement/GetTypes',
contentType: 'application/json; charset=utf-8',
async: false,
cache: false,
dataType: 'json'
}).done(function (data) {
console.log('data = ' + data);
Types = data;
console.log('Types[0].Text = ' + Types[0].Type);
console.log('Types[0].Value = ' + Types[0].Description);
console.log('Types[1].Text = ' + Types[1].Type);
console.log('Types[1].Value = ' + Types[1].Description);
});
});
When I see the console:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.
data = [object Object],[object Object]
account-statement.js:151 Types[0].Text = Receipts
account-statement.js:152 Types[0].Value = Receipts
account-statement.js:153 Types[1].Text = Payment
account-statement.js:154 Types[1].Value = Payment
Controller: AccountStatementController.cs
public JsonResult GetTypes()
{
List<SelectListItem> types = new List<SelectListItem>
{
new SelectListItem
{
Text = "Receipts",
Value = "Receipts"
},
new SelectListItem
{
Text = "Payment",
Value = "Payment"
}
};
}
Can I use SelectListItem rather than create the View Model? I've tried to create AccountStatementTypeViewModel.cs and replace the SelectListItem.
AccountStatementTypeViewModel.cs
public class AccountStatementTypeViewModel
{
public string Type { get; set; }
public string Description { get; set; }
}
update the GetTypes() method
public JsonResult GetTypes()
{
List<AccountStatementTypeViewModel> types = new List<AccountStatementTypeViewModel>
{
new AccountStatementTypeViewModel
{
Type = "Receipts",
Description = "Receipts"
},
new AccountStatementTypeViewModel
{
Type = "Payment",
Description = "Payment"
}
};
return Json(types, JsonRequestBehavior.AllowGet);
}
update the select tag in _AccountStatement.cshtml
<select class="form-control"
data-bind="options: Types, optionsValue: 'Type', optionsText: 'Description', value: Type"></select>
but the result is same, it cannot bind into the DropDownList value.
I've followed the code from this site and download the source code and see the code how do they fill the Phone Type dropdownlist. The tutorial use ASP.NET MVC 4, jQuery 1.8.3, and Knockout 2.2.0, but mine using ASP.NET MVC 5, jQuery 2.1.4, and Knockout 3.3.0. Is there any problem with the version differences?
My Html
#using (Html.BeginForm("userrights", "Users", FormMethod.Post, new { #class = "form-horizontal", role = "form", #id = "userrights_frm", #name = "userrights_frm" }))
{
<div class="form-group">
<div class="col-md-7" style="padding-right:10px">
#Html.TextBoxFor(m => m.firstname, new { #class = "form-control", #id = "firstname", #name = "firstname", #placeholder = "Serach By User Name" })
#Html.HiddenFor(m => m.id, new { #class = "form-control", #id = "id", #name = "id" })
</div>
<div class="col-sm-2">
<button type="submit" id="btn_search" value="Search" name="btn_search" class="btn btn-default">Search</button>
</div>
<div class="clearfix"></div>
</div>
<div class="form-group">
<div class="col-md-1" style="padding-top:7px"> Select Top Link </div>
<div class="col-sm-6">
#Html.DropDownList("selectedMenu", Model._menu.Select(menu => new SelectListItem { Text = menu.menuname, Value = menu.menuid.ToString() }), "Select Menu", new { #class = "form-control" })
</div>
<div class="clear"></div>
</div>
#Html.Partial("_userRightPartial", Model._UserRightPartialView)
<div class="clear"></div>
}
AjaxMethod on Select Menu Changed
$("#selectedMenu").change(function () {
mnid = $(this).val();
userId = $("#userid").val();
$.ajax({
type: 'POST',
url: "/Users/getsubmenus",
data: "{userid:'" + userId + "',menuid:'" + mnid + "'}",
contentType: "application/json; charset=utf-8",
dataType: 'json',
success: function (data) {
if (data.result == "Error") {
alert(data.message);
}
}
});
});
Action Result called in Ajax "getsubmenus"
public ActionResult getsubmenus(string userid, string menuid)
{
userright _userright = new userright();
user _user = _ftwCommonMethods.GetuserDataById(Convert.ToInt32(userid));
UserRightPartialView _UserRightPartialView = new UserRightPartialView();
if (_user!=null)
{
IEnumerable<SubMenuListModel> _SubMenuListModel = _ftwCommonMethods.GetSubMenuItemsByMenu(_user.id.ToString(), menuid);
_UserRightPartialView.firstname = _user.firstname;
_UserRightPartialView.userid = _user.id;
_UserRightPartialView._SubMenuListModel = _SubMenuListModel;
}
return PartialView("_userRightPartial", _UserRightPartialView);
}
ActionResult When i click on Search Button
[HttpPost]
[AuthorizeUser]
public ActionResult userrights(FormCollection form_collection)
{
IEnumerable<MenuListModel> _MenuListModel = _ftwCommonMethods.GetMenuItems();
int userid = 0;
if (!string.IsNullOrEmpty(form_collection["id"].ToString()) && form_collection["id"].ToString() != "0")
{
userid = Convert.ToInt32(form_collection["id"].ToString());
}
user _user = _ftwCommonMethods.GetuserDataById(userid);
UserRightViewSearch _UserRightViewSearch = new UserRightViewSearch();
_UserRightViewSearch._menu = _MenuListModel;
if (_user != null)
{
IEnumerable<SubMenuListModel> _SubMenuListModel = _ftwCommonMethods.GetSubMenuItems(_user.id.ToString());
UserRightPartialView _UserRightPartialView = new UserRightPartialView();
_UserRightPartialView.firstname = _user.firstname;
_UserRightPartialView.userid = _user.id;
_UserRightViewSearch.id = _user.id;
_UserRightPartialView._SubMenuListModel = _SubMenuListModel;
_UserRightViewSearch._UserRightPartialView = _UserRightPartialView;
}
else
{
ViewData["message"] = "User Not Found.";
}
return View(_UserRightViewSearch);
}
Partial View
#model FTW_Admin.Models.UserRightPartialView
#if (Model.firstname != null)
{
<div class="dhe-example-section" id="ex-1-2">
<div id="example-1-2">
<div class="col-md-6" style="padding:0px 10px 10px 0px">
<div class="drag_to">#Model.firstname
#Html.Hidden("userid", Model.userid, new { #id = "userid" })
</div>
<div class="middle_section">
<div class="inner_section_page_sec">
<div id="container" class="main_page_area">
<div class="column left first">
<div>
<ul class="sortable-list-allow">
#foreach (var row in Model._SubMenuListModel)
{
if (row.allowd)
{
<li class="sortable-item loop_content">
<table id="dragable_grid">
<tr>
<th style="color:#000">
#row.submenuname
#Html.Hidden("submenuid", #row.submenuid, new { #id = "submenuid" })
#Html.Hidden("menuid", #row.submenuid, new { #id = "menuid" })
</th>
<th class="heading_style_a" style="text-align:right"><span class="allowedSpan"> Allowed</span></th>
</tr>
</table>
</li>
}
}
</ul>
<div class="clear"></div>
</div>
</div>
<div class="clear"></div>
</div>
</div>
</div>
</div>
<div class="col-md-6" style="padding:0px 10px 10px 0px">
<div class="drag_to">Drag and Drop section to home screen section</div>
<div class="inner_section_page_sec">
<div class="main_page_area">
<div id="container">
<div class="column left">
<ul class="sortable-list-disallow">
#foreach (var row in Model._SubMenuListModel)
{
if (!row.allowd)
{
<li class="sortable-item loop_content">
<table id="dragable_grid">
<tr>
<th style="color:#000">
#row.submenuname
#Html.Hidden("submenuid", #row.submenuid, new { #id = "submenuid" })
#Html.Hidden("menuid", #row.submenuid, new { #id = "menuid" })
</th>
<th class="heading_style_a" style="text-align:right"><span class="allowedSpan">Denied</span></th>
</tr>
</table>
</li>
}
}
</ul>
</div>
</div>
<div class="clear"></div>
</div>
</div>
</div>
<div class="clear"></div>
</div>
</div>
}
The thing is when i search records i get all sub menu items in partial view initially. Then i want to do is when i select particular menu link in dropdown partial view should be updated only with submenus of selected menu
Your ajax function specifies a return type of json but your calling a controller method that returns a view. Then in the success callback, you refer to the result property which does not exist. Instead you need to update the DOM with the partial view your returning. In addition, there is no need to stringify the data you pass to the controller. You script should be
$("#selectedMenu").change(function () {
mnid = $(this).val();
userId = $("#userid").val();
$.ajax({
type: 'POST',
url: '#Url.Action("getsubmenus", "Users")', // don't hard code url's
data: { userid: userId, menuid: mnid }, // no quotes
dataType: 'html', // change this
success: function (data) {
$(someElement).html(data); // modify selector to suit
}
});
});
Side note: Your partial is rendering controls with duplicate id attributes (invalid html) and duplicate name attributes without indexers so will not be able to bind to a model when you submit the form, although its unclear what the point of all those hidden inputs is since there is nothing that can be edited so you just posting back exactly the same data unchanged (degrading your app and opening yourself to over-posting attacks)