How to create page using Kendo Gird and Tabstrip - kendo-ui

I want to select a row of grid and show detail row at tabstrip. The same image should attach.
Can anyone help me? I am using asp.net mvc 4 + Kendo Ui controls.
Image is here.
File CandidateController.cs
public ActionResult Index()
{
return View();
}
public ActionResult Can_Read([DataSourceRequest]DataSourceRequest request)
{
return Json(GetAllCan().ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
public ActionResult Can_ReadId([DataSourceRequest]DataSourceRequest request, Guid id)
{
return Json(GetCanById(id).ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
In partial view: Candidate Info
#model RecruitmentOnlineMVC.Models.CandidateViewModel
<div class="candidate-detail" style="width: 827px!important">
<table>
<tr>
<th>Candidate Name</th>
<td>#(Html.TextBoxFor(model => model.CandidateName, new { #class = "k-input k-textbox" }))</td>
<th>ID</th>
<td>#Html.TextBoxFor(model => model.ID, new { #class = "k-input k-textbox" })</td>
</tr>
<tr>
<th>Email</th>
<td>#Html.TextBoxFor(model => model.Email, new { #class = "k-input k-textbox" })</td>
<th>Phone</th>
<td>#Html.TextBoxFor(model => model.Phone, new { #class = "k-input k-textbox" })</td>
</tr>
</table>
</div>
In View Index
#model RecruitmentOnlineMVC.Models.CandidateViewModel
#(Html.Kendo().Splitter()
.Name("splitter")
.Panes(panes =>
{
panes.Add()
.Content(#<div>
#(Html.Kendo().Grid<RecruitmentOnlineMVC.Models.CandidateViewModel>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(c => c.CandidateName);
columns.Bound(c => c.ID);
})
.Selectable(selectable => selectable
.Mode(GridSelectionMode.Single))
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Read(read => read.Action("Can_Read", "Candidate"))
)
.Pageable(pageable => pageable.ButtonCount(3))
.Filterable()
.Sortable()
.ColumnMenu()
.Events(e => e.Change("onSelected"))
)
</div>)
.Scrollable(true)
.Collapsible(true)
.Size("370px");
panes.Add()
.Content(#<div>
#(Html.Kendo().TabStrip()
.Name("tabstrip")
.Items(tabstrip =>
{
tabstrip.Add().Text("Candidate Detail")
.Selected(true)
.LoadContentFrom("/Candidate/CandidateInfo");
tabstrip.Add().Text("Work History")
.LoadContentFrom("/Candidate/WorkHistory");
})
)
</div>);
}))
Script
<script>
function onSelected() {
var can = this.select();
var propId = this.dataItem(can).ID;
\\In here, I want call action at Controller with propId and return detail on Tabstrip
Can anyone help me?
Thank so much !
I want to handled according to process like this :
Please guide me. Thank so much!

You need to make a seprate function in you controller with Json http post
and calls ajax request from the OnSelected() function..
following is the example
//Get Company List
[HttpPost]
public JsonResult GetCandidateDetails(string propId)
{
var Model= repository.GetCnadidatedetails(propId);
return Json(Model), JsonRequestBehavior.AllowGet);
}
<script>
function onSelected() {
var can = this.select();
var propId = this.dataItem(can).ID;
var dataItem = this.dataItem(e.item.index());
$.ajax({
url: '#Url.Action("GetCnadidatedetails", "Controller")',
//note: only string type is allowed as paramater to send to controller
data: { propId : this.propId },
dataType: "json",
type: "POST",
statusCode: {
404: function () {
showMessage("page not found.");
}
},
error: function () {
alert("error");
},
success: function (result) {
//add your result to tabstripe
$("#tabstrip").add(result.Id+result.Name);
}
});

Related

How to fire Ajax function with radiobutton click MVC

I have a View made up of 3 Partial Views:
Partial View #1 is listing RadioButtons. When I click a button I would like to send the resulting selection to the controller in order to update the Partial View #2 (which is a Telerik grid). Then the selection in PV #2 will update Partial View #3 (another Telerik grid).
I am currently doing a Window.location to send selected values to the Controller which causes the Controller and ViewModel to be reloaded and lose state. Other than losing the state, the flow works as I would like.
Is there a way to do this in Ajax to where I do not have to reload the Controller?
I will spare you the Partial Views #2 & #3 of the Telerik grids. If I can get the answer to how to get the radio button to call the Controller without a postback, I can apply the same to those Partial Views.
View:
#model MyProject.ViewModel.MaterialsListVM
#{
ViewBag.Title = "Materials List";
}
<div>
<div class="row">
<div class="col-sm-2">
#Html.Partial("MaterialTypeFilter")
</div>
<div class="col-lg-10">
#Html.Partial("MaterialsGrid")
</div>
</div>
<div class="row"> </div>
<div class="row">
<div class="col-sm-2"> </div>
<div class="col-lg-10">
#Html.Partial("ProjectMaterialsGrid")
</div>
</div>
</div>
Partial View #1:
#model MyProject.ViewModel.MaterialsListVM
<div class="container">
<h5> #Html.Label("Material Type: ", new { style = "font-weight:bold;" })</h5>
#if (Model != null)
{
for (int i = 0; i < Model.Material_Type.MaterialTypeList.Count; i++)
{
<span class="btn-group-sm">
#Html.RadioButton("MatType", new { Model.Material_Type.MaterialTypeList[i].Name }, Model.Material_Type.MaterialTypeList[i].Selected, new { #onclick = "CallChangefunc(this.value)" })
#Html.Label(Model.Material_Type.MaterialTypeList[i].Name)
</span><br />
}
}
</div>
<script>
function CallChangefunc(value) {
var selected = value.replace("{ Name = ", "");
selected = selected.replace("}", "");
window.location = '#Url.Action("Index", "MaterialsList")' + '?selectedMatType=' + selected;
//alert("val is:" + selected);
}
Update
I changed the JavaScript to this Ajax call and that called my Controller HttpPost Index() function:
$(document).ready(function () {
$(':radio[name="MatType"]').change(function (e) {
$.ajax({
type: 'POST',
url: '/MaterialsList/Index',
data: { selectedMatType: $(':radio[name="MatType"]:checked').val()},
dataType: "json",
success: function (data) {
alert('did it');
}
});
});
})
Unfortunately the Controller is still being reloaded and I'm losing the state of the data selected into the Partial View #3 Telerik grid...
...any help would be much appreciated...
Update #2
This solution sucks but I don't know what else to do...
The goal is to retain the state of the datasource for a Telerik grid in Partial View #3. The datasource is a ViewModel with a List of class objects.
Added Partial View #2 code and ActionResult from Controller. There is no HttpPost Index function. Every radio button change fires the ActionResult Index. Every select event in Telerik grid fires ActionResult Index.
Pertinent Controller code:
public ActionResult Index(string selectedMatType, string selectedMaterials, string projectMaterialsList)
{
if (projectMaterialsList != null)
{
materialsListVM.ProjectMaterialsList = JsonConvert.DeserializeObject<List<ProjectMaterialsListVM.ProjectMaterial>>(projectMaterialsList);
}
if (selectedMatType != null)
{
materialsListVM.SelectMaterialType(selectedMatType);
materialsListVM.GetMaterials();
}
if (selectedMaterials != null)
{
string[] materialIds = selectedMaterials.Split(',');
foreach (string id in materialIds)
{
MoveToProjectMaterialsList(id, selectedMatType);
}
}
ViewBag.ProjectMaterialsList = materialsListVM.ProjectMaterialsList;
ViewBag.SelectedMatType = selectedMatType;
return View(materialsListVM);
}
Partial View #2 code:
#model MyProject.ViewModel.MaterialsListVM
<div>
#if (ViewBag.SelectedMatType == "Cap Weld")
{
#(Html.Kendo().Grid(Model.CapWeld_Materials)
.Name("grid")
.Columns(columns =>
{
columns.Select().Width(40);
columns.Bound(c => c.MaterialId).Hidden();
columns.Bound(c => c.MaterialTypeName);
columns.Bound(c => c.PcsPartNum);
columns.Bound(c => c.ClientPartNum);
columns.Bound(c => c.Type).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.OuterDiameter).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.WallThickness).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.Specification).Filterable(ftb => ftb.Multi(true));
columns.Bound(c => c.Grade).Filterable(ftb => ftb.Multi(true));
})
.Events(ev => ev.Change("onChange"))
.Pageable()
.Sortable()
.Scrollable()
.TableHtmlAttributes(new { width = "100%" })
//.HtmlAttributes(new { style="height:500px"})
.PersistSelection(true)
.Filterable()
.DataSource(datasource => datasource
.Ajax()
.ServerOperation(false)
.Model(m => m.Id(d => d.MaterialId))
)
);
}
</div>
#{
var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var val = jss.Serialize(ViewBag.ProjectMaterialsList);
}
<script>
function onChange(arg) {
var selectedMatType = '#(ViewBag.SelectedMatType)';
var projectMaterialsList = '#Html.Raw(val)';
//var obj = $.parseJSON(val);
var grid = $('#grid').data('kendoGrid');
var selectedMaterials = grid.selectedKeyNames().join(", ");
// alert(selectedMaterials);
#*$.ajax({
type: "POST",
data: { selectedMatType: selectedMatType, selectedMaterials: selectedMaterials, projectMaterialsList: projectMaterialsList },
dataType: "json",
url: #Url.Action("Index", "MaterialsList")
});*#
window.location = '#Url.Action("Index", "MaterialsList")' + '?selectedMatType=' + selectedMatType + '&selectedMaterials=' + selectedMaterials
+ '&projectMaterialsList=' + projectMaterialsList;
}
</script>

am not able to mapping the auto complete textbox value in mvc

Actually when i debugging my code it is hitting the json action but searching value is passing like \"\a\" this,what is the problem am not getting, anybody help me
this is my view:
#model IEnumerable<autoCompelte.Models.Student>
<script type="text/javascript">
$(document).ready(function() {
$("#search").autocomplete({
source: function (request, response) {
$.ajax({
url: "/Home/AutoComplte",
data: {term :JSON.stringify( request.term)},
type: "POST",
datatype:"json",
success: function (data) {
response($.map(data, function (item) {
return {
label: item.Description,
value: item.Id
}
}))
},
error: function (xhr, ajaxOptions, thrownError) {
alert('Failed to retrieve states.');
}
})
}
})
})
</script>
....
<p><input type="text" id="search" name="search"></p>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.Name)</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
This is my controller
public ActionResult Index()
{
return View(db.Students.ToList());
}
public JsonResult AutoComplte( string term)
{
var result = from n in db.Students
where n.Name.ToLower().StartsWith(term.ToLower())
select n.Name;
return Json(result, JsonRequestBehavior.AllowGet);
}

Kendo grid dropdown issue

I have a view with tabs and each tab has a kendo grid with inline edit and have dropdown inside the grid. Problem which i am facing is when i update the value of the drop down is not showing the updated value but when i refresh the page the updated value is showing. But if i refresh the problem here is if i update the value is the sceond tab it i do refresh it will go to second tab. Is there any way I can acheive this without refesh.
#model BHI.TESQ.Entities.Interfaces.Models.ILocationAdminModel
#if (TempData["Result"] == "Data Saved Successfully")
{
<script>
alert('Location Data Saved Successfully');
</script>
}
#using (Html.BeginForm("SaveLocationAdmin", "Administration", FormMethod.Post))
{
<div style="margin-top: 45px;height:400px">
<h5 class="page-header k-block k-primary" style="overflow: hidden; margin-left: 5px; max-width: 1133px;">#ViewBag.Title</h5>
<table style="width: 100%; margin-top: 0px">
<tr style="margin-top: 0px;">
<td>
#*<table style="width: 100%">
<tr>
<td>*#
#(Html.Kendo().Splitter()
.Name("vertical")
.HtmlAttributes(new { style = "min-height:400px" })
.Orientation(SplitterOrientation.Horizontal)
.Panes(verticalPanes =>
{
verticalPanes.Add()
.Size("150px")
.HtmlAttributes(new { style = "min-height:400px" })
.Scrollable(false)
.Resizable(false)
.Content(Html.Action("AdminLinks").ToString());
verticalPanes.Add()
.HtmlAttributes(new { id = "top-pane" })
.Scrollable(false)
.Collapsible(false)
.Content(
Html.Kendo().Splitter()
.Name("horizontal")
.HtmlAttributes(new { style = "min-height:400px" })
.Panes(horizontalPanes =>
{
horizontalPanes.Add()
.HtmlAttributes(new { id = "left-pane" })
.Size("300px")
.Collapsible(true)
.Resizable(false)
.Content(#<div class="pane-content">
<h6 class="header k-block">Create New Location</h6>
<table>
<tr>
<td class="td-style">Location Name<span class="asterisk_input"></span></td>
<td>
<input id="txtLocName" name="txtLocName" type="text" class="textbox" required />
</td>
</tr>
<tr>
<td class="td-style">Country<span class="asterisk_input"></span></td>
<td>
#(Html.Kendo().DropDownList()
.Name("ddCountry")
.HtmlAttributes(new { required = "required" })
//.BindTo(new List<string>() {
// "United States",
// "United Kingdom (UK)",
// })
.BindTo(#Model.Country)
.OptionLabel("--Select Country--")
.DataTextField("CountryName")
.DataValueField("CountryId")
.Events(e => e.Change("OnCountryChange"))
)
<input type="hidden" id="hddCountry" name="hddCountry" value="" />
</td>
</tr>
<tr>
<td class="td-style">State</td>
<td>
#(Html.Kendo().DropDownList()
.Name("ddState")
.OptionLabel("--Select State--")
.DataTextField("StateName")
.DataValueField("StateId")
.Events(e => e.Change("OnStateChange"))
//.BindTo(new List<string>() {
// "Texas",
// })
.DataSource(source =>
{
source.Read(read => { read.Action("GetStateList", "Administration").Data("filterState"); }).ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("ddCountry")
)
<script>
function filterState() {
return {
countryId: $("#ddCountry").val()
};
}
</script>
<input type="hidden" id="hddState" name="hddState" value="" />
</td>
</tr>
<tr>
<td class="td-style">Location Status<span class="asterisk_input"></span></td>
<td>
#*#(Html.Kendo().DropDownList()
.Name("ddLocStatus")
.BindTo(new List<string>() {
"Active",
"In-Active",
})
)*#
<input id="chkBoxStatus" type="checkbox" name="chkBoxStatus" value="true" />
</td>
</tr>
<tr>
<td align="right">
#(Html.Kendo().Button()
.Name("btnInsert")
.Events(ev => ev.Click("onClick"))
.HtmlAttributes(new { type = "submit", #class = "btn btn-primary button" })
.Content("Save"))
</td>
<td>
#(Html.Kendo().Button()
.Name("btnCancel")
.HtmlAttributes(new { type = "button", #class = "btn btn-primary button" })
.Content("Cancel"))
</td>
</tr>
</table>
</div>
);
horizontalPanes.Add()
.HtmlAttributes(new { id = "center-pane" })
.Scrollable(false)
.Content(#<div>
#(Html.Kendo().Grid(Model.Locations)
.Name("Grid")
.HtmlAttributes(new { style = "min-height:400px" })
.Columns(columns =>
{
columns.Command(command => { command.Edit().HtmlAttributes(new { type = "button", #class = "btn btn-primary button" }); }).Width(100);
columns.Bound(p => p.LocationId).Width(140).Hidden(true);
columns.Bound(p => p.LocationName).Width(140);
columns.Bound(p => p.CountryId).EditorTemplateName("CountryNames").Title("CountryName").ClientTemplate("#:CountryName#").Width(150);
columns.Bound(p => p.StateId).EditorTemplateName("StateNames").Title("StateName").ClientTemplate("#:StateName#").Width(150);
columns.Bound(p => p.IsActive).Width(100);
})
.Editable(editable => editable.Mode(GridEditMode.InLine))
.Pageable()
.Navigatable()
.Sortable()
.Scrollable()
.Groupable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.ServerOperation(false)
.Events(events => { events.Error("error_handler"); events.RequestEnd("onRequestEnd"); })
.Model(model => { model.Id(p => p.LocationId); model.Field(p => p.LocationId).Editable(false); })
.Update(update => update.Action("Update_Location", "Administration"))
)
)
</div>
);
}).ToHtmlString()
);
})
)
</td>
</tr>
</table>
#*</td>
</tr>
</table>
*#
</div>
}
<style scoped>
#vertical {
width: 95%;
}
.pane-content {
width: 300px;
}
div.k-grid-content {
min-height: 300px;
}
</style>
<script type="text/javascript">
function error_handler(e) {
if (e.errors) {
var message = "Errors:\n";
$.each(e.errors, function (key, value) {
if ('errors' in value) {
$.each(value.errors, function () {
message += this + "\n";
});
}
});
alert(message);
var grid = $("#Grid").data("kendoGrid");
grid.cancelChanges();
}
}
$(document).ready(function () {
$('#btnCancel').click(function () {
$(':input').not(':button, :submit, :reset, :hidden').val('')
$('input:checkbox').removeAttr('checked')
var dropdownlist = $("#ddCountry").data("kendoDropDownList");
dropdownlist.select(0);
var dropdownlist = $("#ddState").data("kendoDropDownList");
dropdownlist.select(0);
});
});
function onClick(e) {
var locationName = $("#txtLocName").val();
if (locationName != "") {
var selectedText = $("#ddCountry").data("kendoDropDownList").text();
if (selectedText == "--Select Country--") {
alert('Please select country');
}
}
}
function OnCountryChange(e) {
var selectedText = $("#ddCountry").data("kendoDropDownList").text()
$("#hddCountry").val(selectedText);
}
function OnStateChange(e) {
var selectedText = $("#ddState").data("kendoDropDownList").text()
$("#hddState").val(selectedText);
}
function onRequestEnd(e) {
if (e.type == "create" || e.type == "update") {
if (e.response == null || e.response.Errors == null) {
alert("Location Data Updated Successfully");
}
}
}
</script>
Ok. So taking your code sample and assuming a couple of things:
Your code:
public ActionResult Update_Parameter([DataSourceRequest] DataSourceRequest request, Parameter UpdatedParameter)
{
try
{
mTestAdminLogic.SaveModel((ITestAdminModel)model, ViewBag.ApplicationId, Operation.UpdateParameter);
}
catch (Exception ex)
{
ModelState.AddModelError("ParameterUpdateError", ex.Message);
}
return RedirectToAction("TestAdmin");
}
and these are my changes:
public JsonResult Update_Parameter([DataSourceRequest] DataSourceRequest request, Parameter UpdatedParameter)
{
var returnModel = {your return model here};
try
{
returnModel = mTestAdminLogic.SaveModel((ITestAdminModel)model, ViewBag.ApplicationId, Operation.UpdateParameter);
}
catch (Exception ex)
{
ModelState.AddModelError("ParameterUpdateError", ex.Message);
}
return Json(new[] { returnModel }.ToDataSourceResult(request, ModelState), JsonRequestBehavior.DenyGet);
}
So let me explain.
If this action is only being used to update the Grid then change it to a JsonResult as the result of the grid update will be expecting JSON back.
I am assuming you have left our the declaration of your Data Layer logic and have cut it down to this single line:
mTestAdminLogic.SaveModel((ITestAdminModel)model, ViewBag.ApplicationId, Operation.UpdateParameter);
now if this model is different to the one expected by the grid the resulting action of this line should return back the appropriate model that the grid is expecting. As you have not declared that in your grid I am assuming it is some kind of LocationBasedModel.
Now once this update has completed then you should be returning back the appropriate model and adding the following Using statement using Kendo.Mvc.Extensions; this will give you access to the .ToDataSourceResult() extension method which will then allow you to transform this single model back to the grid and let the grid update its view of the data. Including the request and modelstate will also provide any errors and apply any existing filtering etc. back to the grid.
This would resolve the issue with the page "refreshing" itself everytime this action is called.
Any issues let me know and I will update my answer to reflect any changes you need.

MVC3 reloading part of page with data razor

I have a table with my data from base and 3 buttons to delete, create and update which return PartialViews.
I want to update the part of my page with data after clicking the submit button in the corresponding dialog (delete, update, ...).
What is the easiest way to achive this?
This is what I've got now
I will just add, delete is mostly the same.
<div id="delete-dialog" title="Delete Product"></div>
<script type="text/javascript" >
$(".deleteLink").button();
var deleteLinkObj;
// delete Link
$('.deleteLink').click(function () {
deleteLinkObj = $(this);
var name = $(this).parent().parent().find('td :first').html();
$('#delete-dialog').html('<p>Do you want delete ' + name + ' ?</p>');
//for future use
$('#delete-dialog').dialog('open');
return false; // prevents the default behaviour
});
$('#delete-dialog').dialog({
dialogClass: "ConfirmBox",
autoOpen: false, width: 400, resizable: false, modal: true, //Dialog options
buttons: {
"Continue": function () {
$.post(deleteLinkObj[0].href, function (data) { //Post to action
if (data == '<%= Boolean.TrueString %>') {
deleteLinkObj.closest("tr").hide('fast'); //Hide Row
}
else {
}
});
$(this).dialog("close");
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
</script>
And after dialog close I want something like a reload of a part of the page.
The data looks like
<table>
<tr>
<th> Name </th>
<th> Date </th>
<th> </th>
</tr>
#foreach (var m in this.Model)
{
<tr>
<td>
<div class="ProductName">#Html.DisplayFor(Model => m.Name)</div>
</td>
<td>
#Convert.ToDateTime(m.AddDate).ToShortDateString()
</td>
<td>
<div class="ProductPrice">#string.Format("{0:C}", m.Price)</div>
</td>
<td>
<div class="CategoryName">#Html.DisplayFor(Model => m.CategoryName)</div>
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = m.ID }, new { #class = "editLink" })
#Html.ActionLink("Delete", "Delete", new { id = m.ID }, new { #class = "deleteLink" })
</td>
</tr>
}
</table>
I'm not sure if im doing this well
I tried to put this action after click the button but nut sure if is right
I changed the Index to a Partial View
buttons: {
"Continue": function () {
$.post(deleteLinkObj[0].href, function (data) { //Post to action
if (data == '<%= Boolean.TrueString %>') {
deleteLinkObj.closest("tr").hide('fast'); //Hide Row
}
else {
}
});
$.ajax.ActionLink("Index",
"Index", // <-- ActionMethod
"Shop", // <-- Controller Name.
new { }, // <-- Route arguments.
null // <-- htmlArguments .. which are none. Y
)
$(this).dialog("close");
},
"Cancel": function () {
$(this).dialog("close");
}
}
I suggest you use the ASP.NET MVC ActionLink helper in your .cshtml file, and not jQuery:
[...]
<script type="text/JavaScript">
function openPopup()
{
// Set your options as needed.
$("#yourPopupDialogId").dialog("open");
}
</script>
[...]
#Ajax.ActionLink(
"Delete",
"Delete",
"Controller",
new { someValue = 123 },
new AjaxOptions
{
// Set your options as needed
HttpMethod = "GET",
UpdateTargetId = "yourPopupDialogId",
OnSuccess = "openPopup()"
}
)
[...]
<div id="yourPopupDialogId" style="display: none;"></div>
Now in your Controller for the methods you want to use for your popups you should return PartialViews:
public ActionResult Delete(int id)
{
RecordDeleteModel model = YourRepository.GetModel( id );
return PartialView( model );
}

AJAX postback response different than model data

I have a partial view that has textfields in it and is bound to a List set up, so essentially there is a row of textfields that the user can add another row to or remove a row from. I build the view by looping through the list like this:
#model PricingRequestWebApp.Web.Models.PricingRequestModel
<div id="shiplocation-wrapper">
<table class="shipinfoprtable">
<thead>
<tr>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].Address)<span class="formRed">*</span></th>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].City)<span class="formRed">*</span></th>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].State)<span class="formRed">*</span></th>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].Zip)<span class="formRed">*</span></th>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].PFreight)<span class="formRed">*</span></th>
<th>#Html.LabelFor(m => m.ShippingLocationsModel[0].FreightType)<span class="formRed">*</span></th>
<td><span class="link" id="addlocation" onclick="AddShippingLocation()">Add</span></td>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.ShippingLocationsModel.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].Address, new { #style = "width: 200px" })</td>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].City, new { #style = "width: 150px" })</td>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].State, new { #size = 3 })</td>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].Zip, new { #style = "width: 80px" })</td>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].PFreight, new { #style = "width: 80px" })</td>
<td>#Html.TextBoxFor(m => m.ShippingLocationsModel[i].FreightType, new { #style = "width: 200px" })</td>
#if (Model.ShippingLocationsModel.Count > 1)
{
<td><span class="link" id="removelocation" onclick="RemoveShippingLocation(#i);">Remove</span></td>
}
</tr>
}
</tbody>
</table>
</div>
My controller methods look like so:
[HttpPost]
public ActionResult AddShippingLocation(PricingRequestModel model)
{
model.ShippingLocationsModel.Add(new ShippingLocationsModel());
return PartialView("shiplocationsPartial", model);
}
[HttpPost]
public ActionResult RemoveShippingLocation(PricingRequestModel model)
{
model.ShippingLocationsModel.RemoveAt(StaticItems.id);
return PartialView("shiplocationsPartial", model);
}
And my Jquery function that posts the data and returns the view:
function AddShippingLocation() {
$.ajax({
data: $('#shippinginfoform').serialize(),
type: "POST",
url: "/PricingRequest/AddShippingLocation",
success: function (response) {
$('#shiplocation-wrapper').html(response);
}
})
}
function RemoveShippingLocation(id) {
$.ajax({
data: { id: id },
type: "POST",
url: "/PricingRequest/SaveID",
complete: function () {
$.ajax({
data: $('#shippinginfoform').serialize(),
cache: false,
type: "POST",
url: "/PricingRequest/RemoveShippingLocation",
success: function (response) {
$('#shiplocation-wrapper').html(response);
}
})
}
})
}
Now, lets say I have 4 items created in the user view. I click on item 2 to remove it and it passes in i as an id. I then remove that from the model list and pass back the view with the updated model. I have debugged this at least 50 times now and the controller method looks exactly as it should and the data in the view as well. Inexplicably though the response on the ajax shows differently than what I'm seeing while debugging. Clicking on remove item 2 removes item 4. If I have 5 items, item 5 will be removed no matter what item I click to remove. It's always the last item that is removed. I know I must be missing something here but I can't see what. Any ideas? Thanks.
The problem with your code is that you are using sequential indexes for your collections and not recalculating those indexes when adding/removing elements leaving gaps which prevents the model binder from operating correctly. As an alternative you could use non-sequential indexes as illustrated in the following blog post.

Resources