I've implemented my cascading dropdown list with MVC3 almost exactly as explained in
Easiest way to create a cascade dropdown in ASP.NET MVC 3 with C#
My view had that
<script type="text/javascript">
$(function () {
$('#CategoryID').change(function () {
var selectedCategoryId = $(this).val();
$.getJSON('#Url.Action("SelectCategory")', { categoryid: selectedCategoryId }, function (subcategories) {
var subsSelect = $('#SubCategoryID');
subsSelect.empty();
$.each(subcategories, function (index, subcat) {
subsSelect.append(
$('<option/>')
.attr('value', subcat.SubCategoryID)
.text(subcat.SubCategoryName)
);
});
});
});
});
</script>
My controller had that
public ActionResult SelectCategory(int categoryid)
{
var subs = db.SubCategories.Where(s => s.CategoryID == categoryid).ToList();
return Json(subs, JsonRequestBehavior.AllowGet);
}
And that did not work.
However, it worked when I modified the controller the following way:
public class JsonSubCat
{
public int SubCategoryID { get; set; }
public string SubCategoryName { get; set; }
}
public ActionResult SelectCategory(int categoryid)
{
var subs = db.SubCategories.Where(s => s.CategoryID == categoryid).ToList();
var testsubs = new List<JsonSubCat>();
foreach (var sub in subs)
{
testsubs.Add(new JsonSubCat() { SubCategoryID = sub.SubCategoryID, SubCategoryName = sub.SubCategoryName });
}
return Json(testsubs, JsonRequestBehavior.AllowGet);
}
Looks like a question of converting my entities that I obtain from data source to proper format.
What would be the correct way to implement this?
What would be the correct way to implement this?
I suspect that you have circular references in your domain entities. This is not supported by the JSON serializer, because the JSON format doesn't support circular structures.
You should not pass your domain models to views. Please stop doing this and use view models. Why are you passing your entire subs entity to the view when all that this view cares about is a collection of text and value? That's all a dropdown list needs.
So use view models, not to mention that you already wrote one => the JsonSubCat class which is great:
public ActionResult SelectCategory(int categoryid)
{
var subs = db.SubCategories
.Where(s => s.CategoryID == categoryid)
.ToList()
.Select(x => new JsonSubCat
{
SubCategoryID = x.SubCategoryID,
SubCategoryName = x.SubCategoryName
});
return Json(subs, JsonRequestBehavior.AllowGet);
}
Related
This is my code, built on ASP.NET MVC and Entity Framework:
[HttpPost]
[Route("DeskBooking")]
public JsonResult DeskBooking(string dpStart, string dpEnd, int tmStart, int tmEnd)
{
DateTime dpStartCon = DateTime.Parse(GetDateStart(dpStart));
DateTime dpEndCon = DateTime.Parse(GetDateEnd(dpEnd));
using (Models.nhsdmsEntities ctx = new Models.nhsdmsEntities())
{
List<Models.tblDeskBooking> tblDB = ctx.tblDeskBookings
.Where(x => dpStartCon <= x.DateStart &&
x.DateEnd <= dpEndCon &&
tmStart >= x.TimeStart &&
tmEnd <= x.TimeEnd).ToList();
return Json(new { data = tblDB }, JsonRequestBehavior.AllowGet);
}
}
The tblDB has 3 rows but still on the client side I get this error:
An unhandled exception was generated during the execution of the current web request
[ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.]
Client-side code:
$(document).on("click", "#btnBookDeskEmp", function () {
var dpStart = $("#dpStart").val();
var dpEnd = $("#dpEnd").val();
var tmStart = $("#tmStart").val();
var tmEnd = $("#tmEnd").val();
AjaxReq(function (data) {
}, "DeskBooking", { dpStart: dpStart, dpEnd: dpEnd, tmStart: parseInt(tmStart), tmEnd: parseInt(tmEnd) });
})
function AjaxReq(callback, action, data) {
$.ajax({
url: "/Home/" + action,
method: "POST",
data: data,
})
.done(function (data) {
callback(data);
})
.fail(function (e) {
alert("error");
})
.always(function () {
console.log("complete");
});
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace NHSDMS.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class nhsdmsEntities : DbContext
{
public nhsdmsEntities()
: base("name=nhsdmsEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<tblDesk> tblDesks { get; set; }
public virtual DbSet<tblRoom> tblRooms { get; set; }
public virtual DbSet<tblDeskBooking> tblDeskBookings { get; set; }
}
}
In the edmx file, i had to delete everything from the navigation properties as this was messing up my namesspace. if you would like more info i can show sceenshots.
I read this and tried to implement the ViewDataDictionary in my app but did not work.
In my view, I've the below code:
#{
var myTest = new
{
UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
UserName = "iggy",
};
}
#Html.Partial("~/Partials/test.cshtml", myTest)
and the test.cshtml is very simple, when I write #Model, I get { UserId = cdb86aea-e3d6-4fdd-9b7f-55e12b710f78, UserName = iggy }
How can I extract this as JSON, so I can read #Model.UserName
I tried using:
<script type="text/javascript">
#
{
<text>
var obj = JSON.parse(#Model);
</text>
}
</script>
and tried:
<script type="text/javascript">
#:var obj = JSON.parse(#Model);
</script>
and tried:
#Html.Raw(Json.Encode(object))
but nothing worked, any help!!
I came across similar problem when I was converting my .net application into .net core.
In .net I could just return Json(jsonModel) in my controller and use it in my view as data.UserID, data.UserName (see code below adjusted to match your sample).
In .net core I had to SerializeObject in my controller first and then use JSON.parse in the view javascript section to make it work.
The following works in .NET CORE. Assuming you have some model:
public class SomeModel
{
public string UserId { get; set; }
public string UserName { get; set; }
}
in your controller return Json object:
using Newtonsoft.Json;
[HttpPost]
public IActionResult someAction()
{
SomeModel jsonModel = new SomeModel();
jsonModel.UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78";
jsonModel.UserName = "iggy";
var serializedJsonModel = JsonConvert.SerializeObject(jsonModel);
return Json(serializedJsonModel);
}
and in your view javascript section you can retrieve values from your model:
<script type="text/javascript">
$.post("#Url.Action("someAction", "YourController")",
function (data) {
var oJson = JSON.parse(data);
UserId = oJson.UserId;
UserName = oJson.UserName; });
</script>
If you're only interested in JSON serialization of your anonymous type, you can simply declare the #model of the partial as object.
In your main view:
#{
var myTest = new
{
UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
UserName = "iggy",
};
}
#Html.Partial("~/Partials/test.cshtml", myTest)
And in your test.cshtml partial:
#model object
<script type="text/javascript">
var obj = #Html.Raw(Json.Encode(Model));
</script>
I have two drop down lists which are District & School. I would like whenever I choose a district from the list, the values that are in the school list will change at the same time. I am using ajax to try and post the data to another controller but the school list does not change at all. It contains the names of all schools no matter which district I select. I am thinking it has something to do with the line Schools = new SelectList(db.Schools.ToList(), "schoolID", "name")in my SchoolDistrictInformation controller. Here is what I am working with so far:
The security code is a code that must be entered and corresponds with the selected district. It must match with the code in the database or the form will not be submitted.
View Model:
public class DistrictSchoolListViewModel
{
public SelectList Districts { get; set; }
public SelectList Schools { get; set; }
public string SelectedSchool { get; set; }
public string SelectedDistrict { get; set; }
[Required(ErrorMessage = "This code is required")]
public string DistrictCode { get; set; }
}
Controllers:
public ActionResult SchoolDistrictInformation()
{
var viewModel = new DistrictSchoolListViewModel()
{
Districts = new SelectList(db.Districts.ToList(), "leaID", "name"),
Schools = new SelectList(db.Schools.ToList(), "schoolID", "name")
};
return View(viewModel);
}
[HttpPost]
public ActionResult GetSchools(DistrictSchoolListViewModel model)
{
var selectedDistrict = model.SelectedDistrict;
var schools = findSchools(selectedDistrict);
IEnumerable<SelectListItem> filteredSchools =
schools.Select(m => new SelectListItem { Text = m.name, Value = m.schoolID.ToString() });
return PartialView("SchoolDistrictInformation", filteredSchools);
}
School Table Query:
internal IQueryable<School> findSchools(string district)
{
var query = from School in db.Schools
where School.leaID.Equals(district)
select School;
return query;
}
School District Information View:
#model Staff_Form.Models.DistrictSchoolListViewModel
<h2>Select District and School from list</h2>
<script type="text/javascript" src="/scripts/jquery-1.4.4.js"></script>
<script type="text/javascript">
$('#SelectedDistrict').on('change', function () {
$.ajax({
type: "POST",
url: 'Controller/GetSchools',
data: $(this).val(),
success: function (response) {
$('#SelectedSchool').html(response);
}
});
});
</script>
<div>
<form action="#Url.Action("StaffInformation", "Staff")" method="get">
District: #Html.DropDownListFor(m => m.SelectedDistrict, Model.Districts, "----Select----")
Security Code: #Html.TextBoxFor(m => m.DistrictCode) <br />
School: #Html.DropDownListFor(m => m.SelectedSchool, Model.Schools, "----Select----")
<input type="submit" value="Submit" />
</form>
</div>
Get Schools View:
#model System.Collections.Generic.IEnumerable<System.Web.Mvc.SelectListItem>
#{ Layout = null;}
#foreach (var school in Model)
{
<option value="#school.Value">#school.Text</option>
}
I appreciate any and all help given towards the solution of this question. Thank you!
Your script is before the the element with id="SelectedDistrict" and not wrapped in $(document).ready() so your attaching an event to an element which does not even exist at that point.
Move the script to the bottom of the page (immediately before the closing </body? tag and/or wrap it inside document.ready
$(document).ready(function() { // or $(function() {
$('#SelectedDistrict').on('change', function () {
....
});
});
Side note: Update your version of jquery to a recent version and consider returning json to populate your 2nd dropdown (refer this answer)
You should include the property name when you pass the district back to the GetSchools action
$('#SelectedDistrict').on('change', function () {
$.ajax({
type: "POST",
url: 'Controller/GetSchools',
data: { SelectedDistrict: $(this).val() },
success: function (response) {
$('#SelectedSchool').html(response);
}
});
});
I am using view model to display a dropdownlist and i am also trying to get the value of the selected list, here is my view model
public class CreateJobViewModel
{
public int[] SelectedIndustriesIds { get; set; }
public IList<SelectListItem> IndustriesList { get; set; }
}
My controller
public ActionResult Create()
{
var industryList = repository.GetAllIndustries();
var model = new CreateJobViewModel
{
IndustriesList = industryList.Select(i => new SelectListItem
{
Value = i.IndustryId.ToString(),
Text = i.Name
}).ToList()
};
return View("~/Views/Dashboard/Job/Create.cshtml", model);
}
My post controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreateJobViewModel model)
{
try
{
var job = new Job()
{
Title = "hi",
EmploymentHourId = 1,
LocationId = 1,
Salary = 50,
SalaryPeriodId = 1,
PostCode = 2131,
Role = "world",
Description = "hello",
IsPublished = false,
ShiftId = 1,
WorkDayId = 1,
NumberOfPosition = 5,
Meal = false,
SecondYearVisa = true,
Sponsorship = true,
Accommodation = true,
DurationId = 1,
IndustryExperiencePeriod = 5,
Id = User.Identity.GetUserId(),
};
foreach (int id in model.SelectedIndustriesIds)
{
var industry = repository.Industry(id);
job.Industries.Add(industry);
}
foreach (int id in model.SelectedSpecialRequirementsId)
{
var special = repository.SpecialRequirement(id);
job.SpecialRequirements.Add(special);
}
repository.AddJob(job);
return RedirectToAction("Create");
}
catch
{
return View("~/Views/Dashboard/Job/Create.cshtml");
}
}
Every time i try to submit the selected value, i get Object reference not set to an instance of an object Error on the following line in my view:
#model Taw.WebUI.Models.CreateJobViewModel
#Html.ListBoxFor(m => m.SelectedIndustriesIds, Model.IndustriesList) -- here i get the error
Any reason why?
When you submit the form your throwing an exception (confirmed in the comments) and in the catch block you are returning the view, which throws the exception you are seeing because Model.IndustriesList is null. You need to re-assign the value before you return the view.
Since you need to assign SelectLists in the GET method and in the POST method if you return the view, I tend to re-factor this to a separate method to keep the controller code a bit cleaner. Note the following code is based on your model property being public SelectList IndustriesList { get; set; } which is a bit simpler than building IList<SelectListItem>
private void ConfigureViewModel(CreateJobViewModel model)
{
var industryList = repository.GetAllIndustries();
model.IndustriesList = new SelectList(industryList, "IndustryId", "Name")
// any other common stuff
}
and then in the action methods
public ActionResult Create()
{
var model = new CreateJobViewModel();
ConfigureViewModel(model);
return View(model);
}
public ActionResult Create(CreateJobViewModel model)
{
try
{
....
}
catch
{
ConfigureViewModel(model);
return View(model);
}
}
Note its also good practice to test if the model is valid before attempting to save it
public ActionResult Create(CreateJobViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model); // return the view so the user can correct validation errors
}
....
I previously had the following line of code from within my AdminController that was successfully returning a list of relevant subsections from within a course:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
var Sections = dbcontext.CourseSection.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new
{
sectionID = x.CourseSectionID,
sectionTitle = x.Title
);
return Json(Sections, JsonRequestBehavior.AllowGet);
}
I was informed to take this out of the controller as it was bad practice to call dbcontext and so i moved this to the AdminViewModel. Within my AdminViewModel I have a variable public List CourseSectionList { get; set; } and I am trying to populate this variable with the JSON request details. My code is as follows:
AdminViewModel
public void GetCourseSectionDetails(int courseID)
{
var Sections = dbcontext.CourseSection.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title
});
this.CourseSectionList = Sections.ToList();
}
AdminController
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
avm.GetCourseSectionDetails(courseID);
var Sections = avm.CourseSectionList.Where(cs => cs.CourseID.Equals(courseID)).Select(x => new
{
sectionID = x.CourseSectionID,
sectionTitle = x.Title
});
System.Diagnostics.EventLog.WriteEntry("Application", "JSON=" + Sections.ToList(), System.Diagnostics.EventLogEntryType.Error);
return Json(Sections, JsonRequestBehavior.AllowGet);
}
I am getting the error The entity or complex type 'MetaLearning.Data.CourseSection' cannot be constructed in a LINQ to Entities query. How can I populate this.CourseSectionList variable using the Sections?
As pointed by your error message, you can't, in linq to entities, use a
.Select(m => new <Entity>{bla bla})
where <Entity>... is one of your model's entity.
So either you use a "non model" class (DTO), which has the properties you need, or you have to enumerate before selecting (because linq to objects has not that limitation)
.ToList()
.Select(m => new <Entity>{bla bla});
You can find some nice explanations of why it's not possible here
EDIT :
you may also do something like that, if you wanna retrive only some properties of your entity, and don't wanna use a DTO :
return ctx
.CourseSection
.Where(cs => cs.CourseID.Equals(courseID))
//use an anonymous object to retrieve only the wanted properties
.Select(x => new
{
c= x.CourseSectionID,
t= x.Title,
})
//enumerate, good bye linq2entities
.ToList()
//welcome to linq2objects
.Select(m => new CourseSection {
CourseSectionID = m.c,
Title = m.t,
})
.ToList();
You don't need to repeat the same code in the controller, but directly pass the list to the view.
This being said I am informing you that placing data access code in your view model is even worse practice than keeping it in the controller. I would recommend you having a specific DAL layer:
public interface IRepository
{
public IList<CourseSection> GetSections(int courseID);
}
which would be implemented:
public class RepositoryEF : IRepository
{
public IList<CourseSection> GetSections(int courseID)
{
using (ctx = new YourDbContextHere())
{
return ctx
.CourseSection
.Where(cs => cs.CourseID.Equals(courseID))
.Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title,
})
.ToList();
}
}
}
and finally have your controller take the repository as dependency:
public class SomeController : Controller
{
private readonly IRepository repo;
public SomeController(IRepository repo)
{
this.repo = repo;
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetCourseSections(int courseID)
{
var sections = this.repo.GetSections(courseID);
return Json(sections, JsonRequestBehavior.AllowGet);
}
}
I did this as follows using Darin's answer as a guide:
ViewModel
public void GetCourseSectionDetails(int courseID)
{
this.CourseSectionList = dbcontext.CourseSection.AsEnumerable().Where(cs => cs.CourseID.Equals(courseID)).Select(x => new CourseSection
{
CourseSectionID = x.CourseSectionID,
Title = x.Title
}).ToList();
}
Controller
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetCourseSections(int courseID)
{
var sections = avm.CourseSectionList;
return Json(sections, JsonRequestBehavior.AllowGet);
}