Many to Many Relation Popup - model-view-controller

basically from embedded field and new to MVC/ASP.net, learning.
I have 2 entities with Many to Many relation.
It is working fine i am able to assign relation bet
Heading
ween them using checkbox.
I want to implement the following:
On Create page of Entity 1, Relative Entity 2 list is shown in table with Link and Unlink buttons.
Find below Image:
Link button will open up the popup which will show Entity 2 listing which is not there in the relation with the Entity 1.
User will select the required Entity 2 using checkbox and press 'Submit button.
On pressing Submit button, the selected Entity 2 objects are added to the **Entity 2 ** table in the Create view and popup closes.
On Saving create view will save everything with relation.
I hope I'm not asking too much... Not able to judge.
Thanks in advance.

Already Working:
1) I am able to open the model using bootstrap modal popup approach and pass the Entity 2 list to it.
2.) I am able to display the list in table.
To achieve:
1) Populate Entity 2 list in popup view with objects which are not in the Entity 2 table in the main view.
2) Have Checkbox in Popup table for selection.
3) Get the selected Entity 2 row details to main view without posting to controller.
4) Update Entity 2 table in the main view with the selected rows.
5) Save to table when save button is pressed..
Entity 1:
public partial class JobPost
{
public JobPost()
{
this.JobTags = new HashSet<JobTag>();
}
public int JobPostId { get; set; }
public string Title { get; set; }
public virtual ICollection<JobTag> JobTags { get; set; }
}
Entity 2:
public partial class JobTag
{
public JobTag()
{
this.JobPosts = new HashSet<JobPost>();
}
public int JobTagId { get; set; }
public string Tag { get; set; }
public virtual ICollection<JobPost> JobPosts { get; set; }
}
public class TempJobTag
{
public int JobTagId { get; set; }
public string Tag { get; set; }
public bool isSelected { get; set; }
}
View Model:
public class JobPostViewModel
{
public JobPost JobPost { get; set; }
public IEnumerable<SelectListItem> AllJobTags { get; set; }
private List<int> _selectedJobTags;
public List<int> SelectedJobTags
{
get
{
if (_selectedJobTags == null)
{
_selectedJobTags = JobPost.JobTags.Select(m => m.JobTagId).ToList();
}
return _selectedJobTags;
}
set { _selectedJobTags = value; }
}
}
Entity 1 Controller:
// GET: JobPosts/Create
public ActionResult Create()
{
var jobPostViewModel = new JobPostViewModel
{
JobPost = new JobPost(),
};
if (jobPostViewModel.JobPost == null)
return HttpNotFound();
var allJobTagsList = db.JobTags.ToList();
jobPostViewModel.AllJobTags = allJobTagsList.Select(o => new SelectListItem
{
Text = o.Tag,
Value = o.JobTagId.ToString()
});
return View(jobPostViewModel);
}
// POST: JobPosts/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(JobPostViewModel jobpostView)
{
if (ModelState.IsValid)
{
var newJobTags = db.JobTags.Where(
m => jobpostView.SelectedJobTags.Contains(m.JobTagId)).ToList();
var updatedJobTags = new HashSet<int>(jobpostView.SelectedJobTags);
foreach (JobTag jobTag in db.JobTags)
{
if (!updatedJobTags.Contains(jobTag.JobTagId))
{
jobpostView.JobPost.JobTags.Remove(jobTag);
}
else
{
jobpostView.JobPost.JobTags.Add((jobTag));
}
}
db.JobPosts.Add(jobpostView.JobPost);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(jobpostView);
}
public ActionResult ViewJobPostTagPopUp()
{
var allJobTagsList = db.JobTags.ToList();
foreach (JobTag jobTag in db.JobTags)
{
if (jobTag.JobTagId == 1)
{
allJobTagsList.Remove(jobTag);
}
}
List<TempJobTag> tmpJobTags = new List<TempJobTag>();
foreach (JobTag jobTag in db.JobTags)
{
TempJobTag tmpJT = new TempJobTag();
tmpJT.Tag = jobTag.Tag;
tmpJT.JobTagId = jobTag.JobTagId;
tmpJobTags.Add(tmpJT);
}
return PartialView("JobTagIndex", tmpJobTags);
}
[HttpPost]
//[ValidateAntiForgeryToken]
public JsonResult ViewJobPostTagPopUp(List<TempJobTag> data)
{
if (ModelState.IsValid)
{
}
return Json(new { success = true, message = "Some message" });
}
Main View:
#model MVCApp20.ViewModels.JobPostViewModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>JobPost</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.JobPost.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobPost.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobPost.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AllJobTags, "JobTag", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBoxFor(m => m.SelectedJobTags, Model.AllJobTags)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("+", "ViewJobPostTagPopUp", "JobPosts",
null, new { #class = "modal-link btn btn-success" })
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<script src="~/Scripts/jquery-2.1.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
</script>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Partial Popup View:
#model IEnumerable<MVCApp20.Models.TempJobTag>
#{
ViewBag.Title = "Index";
//Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Tags</h2>
#using (Html.BeginForm())
{
<table id="datatable" class="table">
<tr>
<th>
<input type="checkbox" id="checkAll" />
</th>
<th>
#Html.DisplayNameFor(model => model.Tag)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#*#Html.EditorFor(modelItem => item.isSelected)*#
<input type="checkbox" class="checkBox"
value="#item.isSelected" />
</td>
<td>
#Html.DisplayFor(modelItem => item.Tag)
</td>
</tr>
}
</table>
#*<div>
#Html.ActionLink("Done", "ViewJobPostTagPopUp", "JobPosts",
null, new { #class = "modal-link btn btn-primary" })
</div>*#
<div>
<button type="submit" id="btnSubmit" class=" btn btn-primary">Submit</button>
</div>
}
<script>
$(document).ready(function () {
$("#checkAll").click(function () {
$(".checkBox").prop('checked',
$(this).prop('checked'));
});
});
$(function () {
$('#btnSubmit').click(function () {
var sdata = new Array();
sdata = getSelectedIDs();
var postData = {};
postData[values] = sdata;
$.ajax({
url: '#Url.Action("ViewJobPostTagPopUp")',
type: "POST",
type: this.method,
//data: $(this).serialize(),
data: JSON.stringify(product),
success: function (result) {
alert("success");
},
fail: function (result) {
alert("fail");
}
});
//alert("hiding");
//$('#modal-container').modal('hide');
});
});
function getSelectedIDs() {
var selectedIDs = new Array();
var i = 0;
$('input:checkbox.checkBox').each(function () {
if ($(this).prop('checked')) {
selectedIDs.push($(this).val());
i++;
alert("got" + i);
}
});
return selectedIDs;
}
</script>

Related

Selected Dropdown value is not inserted into database but textbox value does

Here what I have tried to achieve is that a region has many zones so that zone is populated from a database then a user inserts the name of the region in the textbox then he selects different zones from the dropdown list the selected zone value should keep once selected and clicks the submit button. When the submit button has clicked the name of the region is inserted into the database but the selected dropdown list value is not inserted. here is the code that I have tried
the region controller
// GET: Regions
public ActionResult Index()
{
var regions = db.Regions.Include(r => r.Zone);
return View(regions.ToList());
}
// GET: Regions/Create
public ActionResult Create()
{
Regions zones = new Regions();
zones.regioness = PopulateZones();
ViewBag.ZoneID = new SelectList(db.Zones, "ZoneID", "ZoneName");
return View(zones);
}
public List<SelectListItem> PopulateZones()
{
List<SelectListItem> zones = new List<SelectListItem>();
string conString = ConfigurationManager.ConnectionStrings["StoreContext"].ConnectionString;
using (SqlConnection con = new SqlConnection(conString))
{
using (SqlCommand cmd = new SqlCommand("SELECT ID, ZoneName FROM Zones", con))
{
con.Open();
using (SqlDataReader sdr = cmd.ExecuteReader())
{
while (sdr.Read())
{
fruits.Add(new SelectListItem
{
Text = sdr["ZoneName"].ToString(),
Value = sdr["ID"].ToString()
});
}
}
con.Close();
}
}
return zones;
}
[HttpPost]
public ActionResult Create([Bind(Include = "ID,RegionName")] Regions reg)
{
reg.regioness = PopulateZones();
foreach (SelectListItem item in reg.regioness)
{
if (item.Value == reg.ID.ToString())
{
item.Selected = true;
db.Regions.Add(reg);
db.SaveChanges();
break;
}
}
return View(reg);
}
And my Region model is
public class Regions
{
public int ID { get; set; }
public string RegionName { get; set; }
[NotMapped]
public List<SelectListItem> regioness { get; set; }
public int? ZoneID { get; set; }
public virtual Zones Zone { get; set; }
}
and Zone model class
public class Zones
{
[Key]
public int ID { get; set; }
public string ZoneName { get; set; }
public virtual ICollection<Regions> Regions { get; set; }
}
and my create view in region
#model RegionAndZones.Models.Regions
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Regions</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.RegionName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RegionName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.RegionName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ZoneID, "ZoneID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.DropDownList("ZoneID", null, htmlAttributes: new { #class = "form-control" })*#
#Html.ValidationMessageFor(model => model.ZoneID, "", new { #class = "text-danger" })
#Html.DropDownListFor(m => m.ID, new SelectList(Model.regioness, "Value", "Text"), "Please select")
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
is there any one who can help me any help would be appreciated

Ajax call always going in error part

I have demo in mvc where I want to fetch user details based on dropdown, i am using ajax for selectedindex changed event of dropdown to show userdetails in partial view, but ajax call is always going in error part..
Controller :-
public ActionResult Index()
{
var usermodel = new UserModel();
usermodel.listuser = GetUserData();
usermodel.UserId = usermodel.listuser.First().UserId;
usermodel.UserName = usermodel.listuser.First().UserName;
usermodel.UserSalary = usermodel.listuser.First().UserSalary;
return View(usermodel);
}
public PartialViewResult GetUserRecord(int UserId)
{
var userModel = new UserModel();
userModel.listuser = GetUserData();
var user = userModel.listuser.Where(e => e.UserId == UserId).FirstOrDefault();
userModel.UserId = user.UserId;
userModel.UserName = user.UserName;
userModel.UserSalary = user.UserSalary;
return PartialView("_UserTestPartial.cshtml", userModel);
}
private List<UserModel> GetUserData()
{
var listuser = new List<UserModel>();
var user1 = new UserModel();
user1.UserId = 1;
user1.UserName = "Abcd";
user1.UserSalary = 25000;
var user2 = new UserModel();
user2.UserId = 2;
user2.UserName = "bcde";
user2.UserSalary = 35000;
var user3 = new UserModel();
user3.UserId = 1;
user3.UserName = "cdef";
user3.UserSalary = 45000;
listuser.Add(user1);
listuser.Add(user2);
listuser.Add(user3);
return listuser;
}
Model:-
public class UserModel
{
public int UserId { get; set; }
public string UserName { get; set; }
public double UserSalary { get; set; }
public List<UserModel> listuser { get; set; }
public IEnumerable < SelectListItem > UserListItems
{
get
{
return new SelectList(listuser, "UserId", "UserName");
}
}
}
Index View:-
#Html.DropDownListFor(m => m.UserId, Model.UserListItems, "---Select User--- ", new { Class = "ddlStyle", id = "ddlUser" })
<div id="partialDiv">
#Html.Partial("_UserTestPartial")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#ddlUser").on("change", function () {
$.ajax(
{
url: '/User/GetUserRecord?UserId=' + $(this).attr("value"),
type: 'GET',
data: " ",
contentType: 'application/json; charset=utf-8',
success: function (data) {
$("#partialDiv").html(data);
},
error: function () {
alert("error");
}
});
});
});
</script>
Partial View:-
#model Dropdowndemo.Models.UserModel
<fieldset>
<legend>UserModel</legend>
<div class="display-label">
<strong> #Html.DisplayNameFor(model => model.UserId) </strong>
</div>
<div class="display-field">
#Html.DisplayFor(model => model.UserId)
</div>
<div class="display-label">
<strong> #Html.DisplayNameFor(model => model.UserName) </strong>
</div>
<div class="display-field">
#Html.DisplayFor(model => model.UserName)
</div>
<div class="display-label">
<strong> #Html.DisplayNameFor(model => model.UserSalary) </strong>
</div>
<div class="display-field">
#Html.DisplayFor(model => model.UserSalary)
</div>
</fieldset>
Please remove the data: " ", attribute from ajax, if you're not passing any in it.
In the Index remove
#Html.Partial("_UserTestPartial")
This will throw error since you are not passing any model to the partial view in this way.
As you were binding the partial view via ajax call to #partialDiv it will work fine

how to save items of cascading drop down list in database with asp.net mvc

My Drop-Downs works well the problem is when I want to save my form.
here is my controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,TourId,StartDate,EndDate")] TourDate tourDate)
{
if (ModelState.IsValid)
{
db.TourDates.Add(tourDate);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
ViewBag.TourId = new SelectList(db.Tours, "Id", "TourName", tourDate.TourId);
return RedirectToAction("Index", "test");
}
[HttpPost]
public JsonResult GetT(int? id)
{
var tours = db.Tours.Where(e => e.CountryId == id).ToList()
.Select(e => new
{
Id = e.Id,
TourName= e.TourName
}).ToList();
return Json(tours);
}
and here is My form. in this From, selecttag with id=TourId is filling with data in ajax from the outer drop down dropdownId and it works fine
#Html.DropDownList("dropdownId",
Model.Countries.Select(m => new SelectListItem
{
Value = m.Id.ToString(),
Text = m.CountryName
}),
new { #class = "form-control" })
#using (Html.BeginForm("Create", "test"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TourDate</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.TourDate.TourId, "TourId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<select class="form-control" id="TourId"></select>
#Html.ValidationMessageFor(model => model.TourDate.TourId, "", new { #class = "text-danger" })
</div>
</div>
<br />
<div class="form-group">
#Html.LabelFor(model => model.TourDate.StartDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TourDate.StartDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TourDate.StartDate, "", new { #class = "text-danger" })
</div>
</div>
<br />
<div class="form-group">
#Html.LabelFor(model => model.TourDate.EndDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TourDate.EndDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TourDate.EndDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
the problem is when I submit the form no diffrent which Tour is chosen always there is TourId=0.
Appreciate any help and also here is the ajax if needed
$("#dropdownId").change(function () {
$('#TourId').empty();
var countrySelected = $(this).val();
$.ajax
({
url: '/test/GetT/' + countrySelected,
type: 'POST',
data: {
'countryId': countrySelected
},
success: function (data)
{
var $select = $('#TourId');
for (var i = 0; i < data.length; i++)
{
$('<option/>').attr('value', data[i].Id).html(data[i].TourName).appendTo('#TourId');
}
}
});
});
The reason your second <select> element does not submit a value is because it does not have a name attribute. It would need to be
<select class="form-control" name="TourDate.TourId" id="TourId"></select>
However there are multiple other errors and problems with your code. To note just a couple of them:
The model in your view is is not typeof TourDate (its a model
containing a property which is TourDate) so none of your controls
can be bound to your TourDate tourDate parameter in the POST
method (the model in the parameter needs to be the same as the model
in the view, or you need to use the [Bind(Prefix = "TourDate")]
attribute and you also need to remove the [Bind(Include = "..")]
attribute).
Your not getting client side validation associated with your
dropdownlists and in the POST method, if ModelState is invalid,
your just redirecting (the user would just assume the object has
been saved and not understand what is going on). You need to return
the view so errors can be corrected, but in your case, the values
the user has entered will be reset to the defaults (an annoying user
experience).
Your editing data, so you should always use a view model and in the controller method you need to populate the SelectList's to account for initial values and edited values (for when you need to return the view). You code should be
public class TourDateVM
{
[Required(ErrorMessage = "Please select a country")]
[Display(Name = "Country")]
public int? SelectedCountry { get; set; }
[Required(ErrorMessage = "Please select a tour")]
[Display(Name = "Country")]
public int? SelectedTour { get; set; }
[Required(ErrorMessage = "Please enter a start date")]
[Display(Name = "Start date")]
public DateTime? StartDate { get; set; }
.... // ditto above for EndDate
public IEnumerable<SelectListItem> CountryList { get; set; }
public IEnumerable<SelectListItem> TourList { get; set; }
}
And in the controller
public ActionResult Create()
{
TourDateVM model = new TourDateVM();
ConfigureViewModel(model);
return View(model);
}
[HttpPost]
public ActionResult Create(TourDateVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
TourDate tour = new TourDate()
{
TourId = model.SelectedTour,
StartDate = model.StartDate,
EndDate= model.EndDate
};
db.TourDates.Add(tour);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
private ConfigureViewModel(TourDateVM model)
{
var counties = db.Countries;
model.CountryList = new SelectList(counties, "ID", "Name"); // adjust to suit your property names
if (model.SelectedCountry.HasValue)
{
var tours = db.Tours.Where(e => e.CountryId == model.SelectedCountry);
model.TourList = new SelectList(tours, "Id", "TourName");
}
else
{
model.TourList = new SelectList(Enumerable.Empty<SelectListItem>());
}
}
and finally in the view (note that both dropdownlists need to be inside the <form> element)
#model TourDateVM
....
#using Html.BeginForm())
{
....
<div class="form-group">
#Html.LabelFor(m => m.SelectedCountry, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.SelectedCountry, Model.CountryList, "- Please select -", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.SelectedCountry, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.SelectedTour, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.SelectedTour, Model.TourList, "- Please select -", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.SelectedTour, "", new { #class = "text-danger" })
</div>
</div>
....
}
and the script should be
var url = '#Url.Action("GetT")'; // assumes its in the same controller
var tours = $('#SelectedTour');
$('#SelectedCountry').change(function() {
tours.empty();
$.post(url , { id: $(this).val() }, function(data) {
if (!data) {
return;
}
tours.append($('<option></option>').val('').text('- Please select -'));
$.each(data, function(index, item) {
tours.append($('<option></option>').val(item.Id).text(item.TourName));
});
});
})

Posting model data from one View to another in mvc3 using ajx

I want to transfer model data from one View to another View (in a dialog) using Ajax.Actionlink were i am getting null values on Post Action
This is my View
#using (Ajax.BeginForm("", new AjaxOptions { }))
{
for (int i = 0; i < Model.city.Count; i++)
{
<div class="editor">
<p>
#Html.CheckBoxFor(model => model.city[i].IsSelected, new { id = "check-box-1" })
#Html.HiddenFor(model => model.city[i].Id)
#Ajax.ActionLink(Model.city[i].Name, "PopUp", "Home",
new
{
#class = "openDialog",
data_dialog_id = "emailDialog",
data_dialog_title = "Cities List",
},
new AjaxOptions
{
HttpMethod = "POST"
})
#Html.HiddenFor(model => model.city[i].Name)
</p>
</div>
}
}
On using Ajax.Actionlink i am creating a dialog using ajax scripting
My controller class for this View is
public ActionResult Data()
{
var cities = new City[] {
new City { Id = 1, Name = "Mexico" ,IsSelected=true},
new City { Id = 2, Name = "NewJersey",IsSelected=true },
new City { Id = 3, Name = "Washington" },
new City { Id = 4, Name = "IIlinois" },
new City { Id = 5, Name = "Iton" ,IsSelected=true}
};
var model = new Citylist { city = cities.ToList() };
//this.Session["citylist"] = model;
return PartialView(model);
}
another View for displaying Post action data is
#model AjaxFormApp.Models.Citylist
#{
ViewBag.Title = "PopUp";
}
<h2>
PopUp</h2>
<script type="text/javascript">
$(function () {
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
var checkedAtLeastOne = false;
$('input[id="check-box-2"]').each(function () {
if ($(this).is(":checked")) {
checkedAtLeastOne = true;
// alert(checkedAtLeastOne);
}
});
if (checkedAtLeastOne == true) {
// alert("Test");
$('#div1').show();
$(".dialog").dialog("close");
}
else {
// alert("NotSelected");
$('#div1').hide();
$('#popUp').html(result);
$('#popUp').dialog({
open: function () { $(".ui-dialog-titlebar-close").hide(); },
buttons: {
"OK": function () {
$(this).dialog("close");
}
}
});
}
}
});
return false;
});
});
</script>
<div style="display: none" id="div1">
<h4>
Your selected item is:
</h4>
</div>
#using (Ajax.BeginForm(new AjaxOptions { }))
{
for (int i = 0; i < Model.city.Count; i++)
{
<div class="editor">
<p>
#Html.CheckBoxFor(model => model.city[i].IsSelected,new { id = "check-box-2" })
#Html.HiddenFor(model => model.city[i].Id)
#Html.LabelFor(model => model.city[i].Name, Model.city[i].Name)
#Html.HiddenFor(model => model.city[i].Name)
</p>
</div>
}
<input type="submit" value="OK" id="opener" />
}
#*PopUp for Alert if atleast one checkbox is not checked*#
<div id="popUp">
</div>
and my post controller action result class is
[HttpPost]
public ActionResult PopUp(Citylist model)
{
if (Request.IsAjaxRequest())
{
//var model= this.Session["citylist"];
return PartialView("PopUp",model);
}
return View();
}
My model class is
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class Citylist
{
public IList<City> city { get; set; }
}
You are calling Popup action from actionlink. Instead you should place submit button out of for loop. And give your form right action parameters

Asp.Net MVC 3 (Razor, Json, Ajax) Master Detail - detail save failing

I am new to MVC3 and trying to build a simple Invoicing app. The problem with my code is that the Ajax Post is failing and I cant find out why. Stepped through the JQuery code and it seems fine but by the time the POST hits the controller, the Model.IsValid is false. The problem seems to be with the child records. The invoice Master record is being saved to the DB but the InvoiceRow isnt. The problem lies in the SaveInvoice() function.
public class Invoice
{
[Key]
public int InvoiceID { get; set; }
public int ContractID { get; set; }
[Required]
[Display(Name = "Invoice Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
public DateTime InvoiceDate { get; set; }
[Required]
[Display(Name = "Invoice No")]
public int InvoiceNumber { get; set; }
[Required(AllowEmptyStrings = true)]
[Display(Name = "Payment Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
public DateTime PaymentDate { get; set; }
public virtual Contract Contract { get; set; }
public virtual ICollection<InvoiceRow> InvoiceRows { get; set; }
}
public class InvoiceRow
{
[Key]
public int Id { get; set; }
public int InvoiceID { get; set; }
public string RowDetail { get; set; }
public int RowQty { get; set; }
public decimal ItemPrice { get; set; }
public decimal RowTotal { get; set; }
public virtual Invoice Invoice { get; set; }
}
public class InvoiceController : Controller
{
private CyberneticsContext db = new CyberneticsContext();
//
// GET: /Invoice/
public ViewResult Index()
{
var invoices = db.Invoices.Include(i => i.Contract);
return View(invoices.ToList());
}
//
// GET: /Invoice/Details/5
public ViewResult Details(int id)
{
Invoice invoice = db.Invoices.Find(id);
return View(invoice);
}
//
// GET: /Invoice/Create
public ActionResult Create()
{
ViewBag.Title = "Create";
ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName");
return View();
}
//
// POST: /Invoice/Create
[HttpPost]
public JsonResult Create(Invoice invoice)
{
try
{
if (ModelState.IsValid)
{
if (invoice.InvoiceID > 0)
{
var invoiceRows = db.InvoiceRows.Where(ir => ir.InvoiceID == invoice.InvoiceID);
foreach (InvoiceRow row in invoiceRows)
{
db.InvoiceRows.Remove(row);
}
foreach (InvoiceRow row in invoice.InvoiceRows)
{
db.InvoiceRows.Add(row);
}
db.Entry(invoice).State = EntityState.Modified;
}
else
{
db.Invoices.Add(invoice);
}
db.SaveChanges();
return Json(new { Success = 1, InvoiceID = invoice.InvoiceID, ex = "" });
}
}
catch (Exception ex)
{
return Json(new { Success = 0, ex = ex.Message.ToString() });
}
return Json(new { Success = 0, ex = new Exception("Unable to Save Invoice").Message.ToString() });
}
//
// GET: /Invoice/Edit/5
public ActionResult Edit(int id)
{
ViewBag.Title = "Edit";
Invoice invoice = db.Invoices.Find(id);
ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName", invoice.ContractID);
return View("Create", invoice);
}
//
// POST: /Invoice/Edit/5
[HttpPost]
public ActionResult Edit(Invoice invoice)
{
if (ModelState.IsValid)
{
db.Entry(invoice).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ContractID = new SelectList(db.Contracts, "Id", "ContractName", invoice.ContractID);
return View(invoice);
}
//
// GET: /Invoice/Delete/5
public ActionResult Delete(int id)
{
Invoice invoice = db.Invoices.Find(id);
return View(invoice);
}
//
// POST: /Invoice/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Invoice invoice = db.Invoices.Find(id);
db.Invoices.Remove(invoice);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
#model Cybernetics2012.Models.Invoice
... script tags excluded for brevity
<h2 class="h2">#ViewBag.Title</h2>
<script type="text/javascript">
$( document ).ready( function ()
{
// here i have used datatables.js (jQuery Data Table)
$( '.tableItems' ).dataTable
(
{
"sDom": 'T<"clear">lfrtip',
"oTableTools": { "aButtons": [], "sRowSelect": "single" },
"bLengthChange": false,
"bFilter": false,
"bSort": true,
"bInfo": false
}
);
// Add DatePicker widget to InvoiceDate textbox
$( '#InvoiceDate' ).datepicker();
// Add DatePicker widget to PaymentDate textbox
$( '#PaymentDate' ).datepicker();
// Get the tableItems table
var oTable = $( '.tableItems' ).dataTable();
} );
// this function is used to add item to table
function AddInvoiceItem()
{
// Adding item to table
$( '.tableItems' ).dataTable().fnAddData( [$( '#RowDetail' ).val(), $( '#RowQty' ).val(), $( '#ItemPrice' ).val(), $( '#RowQty' ).val() * $( '#ItemPrice' ).val()] );
// clear text boes after adding data to table..
$( '#RowDetail' ).val( "" )
$( '#RowQty' ).val( "" )
$( '#ItemPrice' ).val( "" )
}
// This function is used to delete selected row from Invoice Rows Table and then set deleted item to Edit text Boxes
function DeleteRow()
{
// DataTables.TableTools plugin for getting selected row items
var oTT = TableTools.fnGetInstance( 'tableItems' ); // Get Table instance
var sRow = oTT.fnGetSelected(); // Get Selected Item From Table
// Set deleted row item to editable text boxes
$( '#RowDetail' ).val( $.trim( sRow[0].cells[0].innerHTML.toString() ) );
$( '#RowQty' ).val( jQuery.trim( sRow[0].cells[1].innerHTML.toString() ) );
$( '#ItemPrice' ).val( $.trim( sRow[0].cells[2].innerHTML.toString() ) );
$( '.tableItems' ).dataTable().fnDeleteRow( sRow[0] );
}
//This function is used for sending data(JSON Data) to the Invoice Controller
function SaveInvoice()
{
// Step 1: Read View Data and Create JSON Object
// Creating invoicRow Json Object
var invoiceRow = { "InvoiceID": "", "RowDetail": "", "RowQty": "", "ItemPrice": "", "RowTotal": "" };
// Creating invoice Json Object
var invoice = { "InvoiceID": "", "ContractID": "", "InvoiceDate": "", "InvoiceNumber": "", "PaymentDate": "", "InvoiceRows":[] };
// Set Invoice Value
invoice.InvoiceID = $( "#InvoiceID" ).val();
invoice.ContractID = $( "#ContractID" ).val();
invoice.InvoiceDate = $( "#InvoiceDate" ).val();
invoice.InvoiceNumber = $( "#InvoiceNumber" ).val();
invoice.PaymentDate = $( "#PaymentDate" ).val();
// Getting Table Data from where we will fetch Invoice Rows Record
var oTable = $( '.tableItems' ).dataTable().fnGetData();
for ( var i = 0; i < oTable.length; i++ )
{
// IF This view is for edit then it will read InvoiceId from Hidden field
if ( $( 'h2' ).text() == "Edit" )
{
invoiceRow.InvoiceID = $( '#InvoiceID' ).val();
}
else
{
invoiceRow.InvoiceID = 0;
}
// Set InvoiceRow individual Value
invoiceRow.RowDetail = oTable[i][0];
invoiceRow.RowQty = oTable[i][1];
invoiceRow.ItemPrice = oTable[i][2];
invoiceRow.RowTotal = oTable[i][3];
// adding to Invoice.InvoiceRow List Item
invoice.InvoiceRows.push( invoiceRow );
invoiceRow = { "RowDetail": "", "RowQty": "", "ItemPrice": "", "RowTotal": "" };
}
// Step 1: Ends Here
// Set 2: Ajax Post
// Here i have used ajax post for saving/updating information
$.ajax( {
url: '/Invoice/Create',
data: JSON.stringify( invoice ),
type: 'POST',
contentType: 'application/json;',
dataType: 'json',
success: function ( result )
{
if ( result.Success == "1" )
{
window.location.href = "/Invoice/Index";
}
else
{
alert( result.ex );
}
}
} );
}
</script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Invoice</legend>
#if (Model != null)
{
<input type="hidden" id="InvoiceID" name="InvoiceID" value="#Model.InvoiceID" />
}
<div class="editor-label">
#Html.LabelFor(model => model.ContractID, "Contract")
</div>
<div class="editor-field">
#Html.DropDownList("ContractID", String.Empty)
#Html.ValidationMessageFor(model => model.ContractID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InvoiceDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InvoiceDate)
#Html.ValidationMessageFor(model => model.InvoiceDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InvoiceNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InvoiceNumber)
#Html.ValidationMessageFor(model => model.InvoiceNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PaymentDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PaymentDate)
#Html.ValidationMessageFor(model => model.PaymentDate)
</div>
</fieldset>
<br />
<fieldset>
<legend>Add Invoice Row</legend>
<br />
<label>
Row Detail :</label>
#Html.TextBox("RowDetail")
<label>
Row Qty :</label>
#Html.TextBox("RowQty", null, new { style = "width:20px;text-align:center" })
<label>
Item Price :</label>
#Html.TextBox("ItemPrice", null, new { style = "width:70px" })
<input onclick="AddInvoiceItem()" type="button" value="Add Invoice Item" />
<table id="tableItems" class="tableItems" width="400px">
<thead>
<tr>
<th>
Detail
</th>
<th>
Qty
</th>
<th>
Price
</th>
<th>
Row Total
</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
foreach (var item in Model.InvoiceRows)
{
<tr>
<td>
#Html.DisplayFor(i => item.RowDetail)
</td>
<td>
#Html.DisplayFor(i => item.RowQty)
</td>
<td>
#Html.DisplayFor(i => item.ItemPrice)
</td>
<td>
#Html.DisplayFor(i => item.RowTotal)
</td>
</tr>
}
}
</tbody>
</table>
<br />
<input onclick="DeleteRow()" type="button" value="Delete Selected Row" />
</fieldset>
<p>
<input onclick="SaveInvoice()" type="submit" value="Save Invoice" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
You need to examine the Model and extract the errors. With those in hand you can begin fixing the problems that are causing the model binder to fail.
Here's an extension method I use to dump invalid model errors to the output console
http://pastebin.com/S0gM3vqg

Resources