I have one ViewModels which contain other ViewModels
public class AllProductsViewModel
{
public ICollection<ProductSearchViewModel> ProductSearch { get; set; }
public ICollection<ProductRentViewModel> ProductRent { get; set; }
public ICollection<ProductBuyViewModel> ProductBuy { get; set; }
}
My Controller is:
public ActionResult Index()
{
var listOfProductsBuy = db.ProductsBuy.Select(x => new ProductBuyViewModel
{
Id = x.Id,
Title = x.Title,
MasterImageUrl = x.Images.FirstOrDefault().Url,
Price = x.Price,
Values = x.Value,
}).ToList();
var listOfProductsRent = db.ProductsRent.Select(y => new ProductRentViewModel
{
Id = y.Id,
Title = y.Title,
MasterImageUrl = y.ImagesRent.FirstOrDefault().Url,
Price = y.Price,
Values = y.Value,
}).ToList();
var listOfProductsSearch = db.ProductSearches.Select(z => new ProductSearchViewModel
{
Id = z.Id,
Title = z.Title,
MasterImageUrl = z.ImagesSearch.FirstOrDefault().Url,
Price = z.Price,
Values = z.Value,
}).ToList();
var viewModel = new AllProductsViewModel { ProductBuy = listOfProductsBuy, ProductRent = listOfProductsRent, ProductSearch = listOfProductsSearch };
return View(viewModel);
}
}
And my View:
#model IEnumerable<RealEstateMarket.ViewModels.AllProductsViewModel>
#{
ViewBag.Title = "Home Page";
}
<br />
<div class="row">
<div class="col-md-3">
#foreach (var item in Model)
{
<h3>#item.ProductBuy.Select(x => x.Title)</h3>
<div>
<img height="100" width="120" class="thumbnail" src="#item.ProductBuy.Select(x => x.MasterImageUrl )" />
</div>
<h3>#item.ProductBuy.Select(x => x.Price) #item.ProductBuy.Select(x => x.Values.Currency)</h3>
}
</div>
</div>
A want to take the fields of ProductBuyViewModel,ProductRentViewModel, ProductSearchViewModel
When I start this code i get the error: The model item passed into the dictionary is of type 'RealEstateMarket.ViewModels.AllProductsViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[RealEstateMarket.ViewModels.AllProductsViewModel]'.
change this
#model IEnumerable<RealEstateMarket.ViewModels.AllProductsViewModel>
to
#model RealEstateMarket.ViewModels.AllProductsViewModel
and then you can access to your list
<div class="col-md-3">
#for (int index=0;index<Model.ProductSearch.Count();index++)
{
<h3>#Model.ProductSearch[index].Title</h3>
<div>
<img height="100" width="120" class="thumbnail" src="#Model.ProductSearch[index].MasterImageUrl )" />
</div>
<h3>#Model.ProductSearch[index].Price #Model.ProductSearch[index].Values.Currency)</h3>
}
</div>
Related
I'm working on an MVC Core form where a requester has to define some approvers for his application. When preparing the model for the Get request, I first get the roles for the approvers. Currently, there are always four roles returned:
Category Head
Governance Head
Concessions VP
Commercial EVP
And here is the HttpGet:
[HttpGet]
public async Task<IActionResult> Create()
{
// omitted for brevity...
// Get the SystemRole models (4 models will be returned)
model.ApprovingRoles = (await serviceLookup.GetAllRolesAsync(ct)).ToList();
}
The SystemRoleModel is simply:
public class SystemRoleModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
The view is composed of EditorTemplate as follows:
Create.cshtml -> LetterEditor.cshtml -> LetterAttachmentEditor.cshtml
Create.cshtml:
#model LetterModel
#{
ViewData["Title"] = "Create RL";
}
#Html.EditorFor(m => m, "LetterEditor", new { ShowApprovers = "1", ShowAttachments = "1", ShowButtons = "1" } )
LetterEditor.cshtml:
#model LetterModel
...
<div class="panel-body">
#await Html.PartialAsync("EditorTemplates/LetterAttachmentEditor", new LetterAttachmentUploadViewModel { IsBusy = false, LetterGuid = Model.IdCode.ToString() })
</div>
...
And finally, LetterAttachmentEditor.cshtml:
#model IList<SystemRoleModel>
#for (var i = 0; i < Model.Count; i++)
{
var index = i;
var title = Model[index].Name;
<div class="row">
<div class="col-lg-2 mt-3">
#Html.Label("LetterApprover[" + index + "]", title, new { #class = "control-label" })
</div>
<div class="col-lg-4">
#(Html.Kendo().DropDownList().Name("LetterApprover[" + index + "]")
.DataValueField(nameof(SystemUserModel.Id))
.DataTextField(nameof(SystemUserModel.EmployeeName))
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetUsersByRoleId", "Api", new { roleId = Model[index].Id });
}).ServerFiltering(true);
})
)
</div>
<div class="col-lg-6">
<span asp-validation="" class="text-danger"></span>
#Html.ValidationMessage("LetterApprover[" + index + "]", $"An approver as a {title} is required", new { #class = "text-danger" })
</div>
</div>
}
Also, LetterModel.cs:
public class LetterModel
{
public LetterModel()
{
Approvers = new List<LetterApproverModel>();
}
// omitted for brevity...
public IList<SystemRoleModel> ApprovingRoles { get; set; } = new List<SystemRoleModel>();
}
Now, with that all out of the way, here is the final rendered dropdown (minus the kendo fluff):
<input id="ApprovingRoles_LetterApprover_0_" name="ApprovingRoles.LetterApprover[0]" required="required" type="text" validationmessage="..." data-role="dropdownlist">
<input id="ApprovingRoles_LetterApprover_1_" name="ApprovingRoles.LetterApprover[1]" required="required" type="text" validationmessage="..." data-role="dropdownlist">
<input id="ApprovingRoles_LetterApprover_2_" name="ApprovingRoles.LetterApprover[2]" required="required" type="text" validationmessage="..." data-role="dropdownlist">
<input id="ApprovingRoles_LetterApprover_3_" name="ApprovingRoles.LetterApprover[3]" required="required" type="text" validationmessage="..." data-role="dropdownlist">
If the user submits this form, I need to receive a list of selected IDs from this array of dropdowns. I followed an anti-pattern, so I'm hoping the MVC binding will do its magic here. I just need to figure out the name of the model property that I should add of type List<string>.
How about try to change the name into name="LetterApprover[0]" and name="LetterApprover[1]" and name="LetterApprover[2]" and name="LetterApprover[3]" .
Then you could bind to List<string> LetterApprover
Update
Name is auto-appended by MVC due to sub-editor
How about add js codes to change the input name when you submit the form?
I try it like below, I first add class="form-control" to dropdownlist, add id="save" to button, then:
<script>
var items = document.getElementsByClassName("form-control");
$('#save').click(function () {
for (var i = 0; i < items.length; i++)
{
items[i].setAttribute("name", "LetterApprover")
}
});
</script>
Then bind to List<string> LetterApprover.
I was able to bind the selected values to a model's property upon submission by modifying the prefix added by the MVC engine:
#using DACRL.Domain.Models.BusinessObjects
#model IList<DACRL.Domain.Models.BusinessObjects.SystemRoleModel>
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
#for (var i = 0; i < Model.Count; i++)
{
var index = i;
var name = "SelectedApprover[" + index + "]";
var title = Model[index].Name;
<div class="row">
<div class="col-lg-2 mt-2">
#Html.Label(name, title, new { #class = "control-label" })
</div>
<div class="col-lg-4">
#(Html.Kendo().DropDownList().Name(name)
.Size(ComponentSize.Medium).Rounded(Rounded.Medium).FillMode(FillMode.Outline)
.HtmlAttributes(new { style = "width: 100%" })
.DataValueField(nameof(SystemUserModel.Identifier))
.DataTextField(nameof(SystemUserModel.EmployeeName))
.OptionLabel("Select " + title).Filter(FilterType.Contains)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetUsersByRoleId", "Api", new { roleId = Model[index].Id, sequence = index + 1 });
}).ServerFiltering(true);
})
.Height(500))
</div>
<div class="col-lg-6">
<span asp-validation="" class="text-danger"></span>
#Html.ValidationMessage(name, $"An approver as a {title} is required", new { #class = "text-danger mt-2" })
</div>
</div>
}
The line ViewData.TemplateInfo.HtmlFieldPrefix = ""; allowed me to control the naming and the binding started workinfg
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
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>
Well I've recently come up against an interesting problem that I can't seem to figure out.
The error message I get is:
{"The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type 'System.TimeSpan'."}
This occurs when I try to submit a new entry to the database. So, the details of what is being submitted.
The model class:
public class EventModel
{
[Key]
public int EventID { get; set; }
[DisplayName("Booking title")]
[Required(ErrorMessage="Please provide a title for the booking")]
public string Title { get; set; }
[DataType(DataType.Date)]
[DisplayName("Start date")]
[DisplayFormat(DataFormatString="{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime StartDateTime { get; set; }
[DisplayName("End date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
[IsDateAfter("StartDateTime", true, ErrorMessage="End date must be on or after the start date")]
public DateTime EndDateTime { get; set; }
public long StartTicks { get; set; }
public long EndTicks { get; set; }
[NotMapped]
[DisplayName("Start Time")]
public TimeSpan StartTime
{
get { return TimeSpan.FromTicks(StartTicks); }
set { StartTicks = value.Ticks; }
}
[NotMapped]
[DisplayName("End Time")]
public TimeSpan EndTime
{
get { return TimeSpan.FromTicks(EndTicks); }
set { EndTicks = value.Ticks; }
}
[DefaultValue(2)]
[DisplayName("Booking is")]
public int BookingStatus { get; set; }
[DisplayName("Set recurrence")]
[DefaultValue(false)]
public bool DoesRecur { get; set; }
[DisplayName("Set recurrence type")]
public string Pattern { get; set; }
[DisplayName("Set the day this happens on ")]
public int DayIndex { get; set; }
[DisplayName("Choose the day instance this recurs on")]
public int DayCount { get; set; }
[DisplayName("Day ")]
[NotMapped]
public string Day { get; set; }
[DisplayName("Instance")]
[NotMapped]
public string Instance { get; set; }
// links resource to a user/member
[DisplayName("Booked by")]
[NotMapped]
public string BookerName { get; set; }
public Guid MemberID { get; set; }
// links resource to a resource type
[DisplayName("Resource required:")]
public int ResourceID { get; set; }
}
The action methods in the controller class:
[HttpGet]
public ActionResult Create(DateTime eventDate)
{
var days = from DayOfWeek d in Enum.GetValues(typeof(DayOfWeek))
select new { ID = (int) d, Name = (DayOfWeek)d };
var instance = from DayInstance i in Enum.GetValues(typeof(DayInstance))
select new { ID = (int) i, Name = (DayInstance)i };
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
CreateEventViewModel model = new CreateEventViewModel()
{
Event = new EventModel()
{
StartDateTime = eventDate,
EndDateTime = eventDate,
MemberID = (Guid)mu.ProviderUserKey
},
Resources = DBContext.Resources.ToList(),
Patterns = DBContext.Patterns.ToList(),
ResourceTypes = DBContext.ResourceTypes.ToList()
};
ViewData["dayOfWeek"] = new SelectList(days, "ID", "Name", DayOfWeek.Monday);
ViewData["dayInstance"] = new SelectList(instance, "ID", "Name", DayInstance.First);
return View(model);
}
[HttpPost]
public ActionResult Create(CreateEventViewModel em)
{
if (ModelState.IsValid)
{
// get the resource turn aournd time
double turnAround = rc.GetResourceTurnAround(em.Event.ResourceID);
MembershipUser mu = Membership.GetUser(HttpContext.Profile.UserName);
em.Event.MemberID = (Guid) mu.ProviderUserKey;
em.Event.BookingStatus = 2;
// need to get the time added to the date.
DateTime actualStartPoint = new DateTime(em.Event.StartDateTime.Ticks + em.Event.StartTicks);
DateTime actualEndPoint = new DateTime(em.Event.EndDateTime.Ticks + em.Event.EndTicks);
em.Event.StartDateTime = actualStartPoint;
em.Event.EndDateTime = actualEndPoint;
// add turn around time to the end of the event
em.Event.EndDateTime = em.Event.EndDateTime.AddMinutes(turnAround);
// needed becase these are handled slighty differently to the rest of the model
em.Event.DayIndex = int.Parse(Request.Form.GetValues("dayOfWeek").GetValue(0).ToString());
em.Event.DayCount = int.Parse(Request.Form.GetValues("dayInstance").GetValue(0).ToString());
DBContext.Events.Add(em.Event);
DBContext.SaveChanges();
// get the resource owner
MembershipUser resourceOwner = Membership.GetUser(rc.GetResourceOwnerByID(em.Event.ResourceID));
// email the admin team and the user the details of this booking
// get the email address of the user making the booking
StringBuilder message = new StringBuilder();
message.AppendFormat("Thank you for your booking, this is now being reviewed by the team.\nThe details of your booking are included for confirmation.\n");
message.AppendFormat("Booking Title: {0}\nResource: {1}\n Date: {2} {3} (this includes our turn around time added on)\n", em.Event.Title, rc.GetResourceNameByID(em.Event.ResourceID), actualStartPoint, actualEndPoint);
message.AppendFormat("You can log in at any time to review your bookings.\nYou will receive an email when the team have reviewed this request\nMany thanks\n");
EmailHandler eh = new EmailHandler();
eh.SetRecipient(Membership.GetUser().Email);
eh.AddAdminEmail();
eh.AddBcc(resourceOwner.Email);
eh.SetSubject("Booking Requested");
eh.SetBody(message.ToString());
eh.sendMessage();
return RedirectToAction("Index");
}
else
{
return View();
}
}
Now for the view items - the main view:
#model AssetManager.Models.CreateEventViewModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend id="bookingLegend">Place Booking</legend>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.Title)
#Html.ValidationMessageFor(model => model.Event.Title)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.StartDateTime)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.StartDateTime, new { #class = "date" })
#Html.ValidationMessageFor(model => model.Event.StartDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
#Html.LabelFor(model => model.Event.StartTime)
</div>
<div class="editor-field timeSelector">
#Html.EditorFor(model => model.Event.StartTime)
#Html.ValidationMessageFor(model => model.Event.StartTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.EndDateTime)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.EndDateTime, new { #class = "date" })
#Html.ValidationMessageFor(model => model.Event.EndDateTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label timeSelector">
#Html.LabelFor(model => model.Event.EndTime)
</div>
<div class="editor-field timeSelector">
#Html.EditorFor(model => model.Event.EndTime)
#Html.ValidationMessageFor(model => model.Event.EndTime)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.Label("Select Resource Type")
</div>
<div class="editor-field">
#Html.DropDownList("ResourceTypes", new SelectList(Model.ResourceTypes, "ResourceTypeID", "Title"), "-- Select Resource Type --", new { #id = "ddlResourceTypes" })
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.ResourceID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Event.ResourceID, new SelectList(Enumerable.Empty<SelectListItem>(), "ResourceType", "Name"), "-- Select Resource --", new { #id = "ddlResources" })
#Html.ValidationMessageFor(model => model.Event.ResourceID)
</div>
</div>
<div class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DoesRecur)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Event.DoesRecur)
#Html.ValidationMessageFor(model => model.Event.DoesRecur)
</div>
</div>
<div id="recurType" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.Pattern)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Event.Pattern, new SelectList(Model.Patterns, "PatternCode", "Pattern"), "-- Select Recurrence Pattern --")
#Html.ValidationMessageFor(model => model.Event.Pattern)
</div>
</div>
<div id="recurDayHappens" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DayIndex)
</div>
<div class="editor-field">
#Html.DropDownList("dayOfWeek")
#Html.ValidationMessageFor(model => model.Event.DayIndex)
</div>
</div>
<div id="recurInstance" class="controlcontainer">
<div class="editor-label">
#Html.LabelFor(model => model.Event.DayCount)
</div>
<div class="editor-field">
#Html.DropDownList("dayInstance")
#Html.ValidationMessageFor(model => model.Event.DayCount)
</div>
</div>
<div class="controlcontainer">
<p>
<input class="subButton" type="submit" value="Create" />
<input id="cancelBtn" class="cancelButton" type="button" value="Cancel" onclick="location.href='#Url.Action("Index", "Calendar")'" />
</p>
</div>
</fieldset>
}
Then there is an editor template for the TimeSpan items:
#model TimeSpan
#Html.DropDownList("Hours", Enumerable.Range(0, 24)
.Select(i => new SelectListItem { Value = i.ToString(),
Text = i.ToString(), Selected = Model.Hours == i })) :
#Html.DropDownList("Minutes", Enumerable.Range(0, 60)
.Select(i => new SelectListItem { Value = i.ToString(),
Text = i.ToString(), Selected = Model.Minutes == i }))
And finally a TimeBinder class:
public class TimeBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Ensure there's incomming data
var key_hours = bindingContext.ModelName + ".Hours";
var valueProviderResult_hours = bindingContext.ValueProvider
.GetValue(key_hours);
var key_minutes = bindingContext.ModelName + ".Minutes";
var valueProviderResult_minutes = bindingContext.ValueProvider
.GetValue(key_minutes);
if (valueProviderResult_hours == null || string.IsNullOrEmpty(valueProviderResult_hours.AttemptedValue)
|| valueProviderResult_minutes == null || string.IsNullOrEmpty(valueProviderResult_minutes.AttemptedValue))
{
return null;
}
// Preserve it in case we need to redisplay the form
bindingContext.ModelState.SetModelValue(key_hours, valueProviderResult_hours);
bindingContext.ModelState.SetModelValue(key_minutes, valueProviderResult_minutes);
// Parse
var hours = ((string[])valueProviderResult_hours.RawValue)[0];
var minutes = ((string[])valueProviderResult_minutes.RawValue)[0];
// A TimeSpan represents the time elapsed since midnight
var time = new TimeSpan(Convert.ToInt32(hours), Convert.ToInt32(minutes), 0);
return time;
}
}
That's it, that is all the code that is involved. I am completely baffled as to why this error occurs. Any ideas or suggestions as to the cause and the solution are greatly appreciated.
Many thanks
nathj07
EDIT
Pk, so I tried something differnt with th TimeSpan editor template:
#model TimeSpan?
#Html.DropDownList("Hours", Enumerable.Range(0, 24)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Hours == i : false
})) :
#Html.DropDownList("Minutes", Enumerable.Range(0, 60)
.Select(i => new SelectListItem
{
Value = i.ToString(),
Text = i.ToString(),
Selected = Model.HasValue ? Model.Value.Minutes == i : false
}))
This seems to have overcome this error but now I get an issue a little further down. In the view there is a DropDownList("ResourceTypes"....) This is essentially a dropdownlist that is used to control what appears in the DropDownListFor(model=>model.Event.ResourceID.....) There is a simple piece of JavaScript:
$(document).ready(function () {
$("#ddlResourceTypes").change(function () {
var idResourceType = $('#ddlResourceTypes').val();
$.getJSON("/Resource/LoadResourcesByType", { id: idResourceType },
function (resourceData) {
var select = $("#ddlResources");
select.empty();
select.append($('<option/>', {
value: 0,
text: "-- Select Resource --"
}));
$.each(resourceData, function (index, itemData) {
select.append($('<option/>', {
value: itemData.Value,
text: itemData.Text
}));
});
});
});
});
Now the issue I get is:
Object reference not set to an instance of an object
On the DropDownList("ResourceTypes".....)
Any ideas on this one?
When you POST an invalid form, you end with the code return View().
So you display the same view, without passing a model, the Model will be null. The first time your code really needs a value is in the editor for TimeSpan. That value is a nullable now, but you never test for the case that it is null.
Change the return to:
return View(em);
to pass the model, or use the code from the GET action, to rebuild and pass the model:
return Create(/* Your create date */);
Edit after comment
The error in the ModelBinder might be caused by the lines:
var hours = ((string[])valueProviderResult_hours.RawValue)[0];
var minutes = ((string[])valueProviderResult_minutes.RawValue)[0];
You convert an array to a string[]. I would do the conversion to string as late as possible, and make it more error proof by:
var hours = Convert.ToString(((object[])valueProviderResult_hours.RawValue).FirstOrDefault()) ?? "00";
This will just cast to an array of object, so there's less change to fail. Take the first element, or return null, and convert that to a string using Convert, and if the result is still null, return "00".
Hit breakpoints in your partial views and check the Model object, somewhere you will find a wrong object for a partial view. that is the cause of this error
This works for me but how do I do the same thing using html.dropdownlist?
Notice that the value passed is not the value that is shown to the user.
#model IEnumerable<MVR.Models.ViewIndividual>
<h2>Level1</h2>
<select>
#foreach (var item in Model) {
<option value="#item.Case_Number">#item.Patient_Lastname ,
#item.Patient_Firstname
</option>
}
</select>
As always in an ASP.NET MVC application you start by defining a view model:
public class MyViewModel
{
public string SelectedIndividual { get; set; }
public SelectList Individuals { get; set; }
}
then you write a controller action that populates this view model from some data source or something:
public ActionResult Index()
{
// TODO : fetch those from your repository
var values = new[]
{
new { Value = "1", Text = "item 1" },
new { Value = "2", Text = "item 2" },
new { Value = "3", Text = "item 3" },
};
var model = new MyViewModel
{
Individuals = new SelectList(values, "Value", "Text")
};
return View(model);
}
and finally you have a strongly typed view using strongly typed helpers:
#model MyViewModel
#Html.DropDownListFor(
x => x.SelectedIndividual,
Model.Individuals
)
This being said, because I see that you are not using any view models in your application, you could always try the following ugliness (not recommended, do this at your own risk):
#model IEnumerable<MVR.Models.ViewIndividual>
<h2>Level1</h2>
#Html.DropDownList(
"SelectedIndividual",
new SelectList(
Model.Select(
x => new {
Value = x.Case_Number,
Text = string.Format(
"{0}, {1}",
x.Patient_Lastname,
x.Patient_Firstname
)
}
),
"Value",
"Text"
)
)
Of course such pornography is not something that I would recommend to ever write in a view and I wouldn't recommend even to my worst enemies.
Conclusion: In an ASP.NET MVC application you should always be using view models and strongly typed views with strongly typed helpers (see first part of my answer).
Here is the full example
public class PageModel
{
[Display(Name = "Page ID")]
public Guid ID { get; set; }
[Display(Name = "Page Type ID")]
public Guid PageTypeID { get; set; }
[Display(Name = "Title")]
public string Title { get; set; }
[Display(Name = "Page Type Name")]
public string PageTypeName { get; set; }
[Display(Name = "Html Content")]
public string HtmlContent { get; set; }
public SelectList PageTypeList { get; set; }
}
the C# code
public ActionResult Edit(Guid id)
{
var model = db.Pages.Where(p => p.ID == id).FirstOrDefault();
var typeList = new SelectList(db.PageTypes.OrderBy(s => s.Name).ToList(), "ID", "Name");
var viewModel = new PageModel { PageTypeList = typeList };
viewModel.HtmlContent = model.HtmlContent;
viewModel.ID = model.ID;
viewModel.PageTypeID = Guid.Parse(model.PageTypeID.ToString());
viewModel.Title = model.Title;
return View(viewModel);
}
[HttpPost]
[ValidateInput(false)]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(PageModel page)
{
if (ModelState.IsValid)
{
var model = db.Pages.Where(p => p.ID == page.ID).FirstOrDefault();
model.Title = page.Title;
model.HtmlContent = page.HtmlContent;
model.PageTypeID = page.PageTypeID;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(page);
}
and lastly html
#model competestreet.com.Models.PageModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_LayoutCMS.cshtml";
}
<script type="text/javascript">
$(document).ready(function () {
$('#HtmlContent').ckeditor();
});
</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>
<script src="#Url.Content("~/Scripts/ckeditor/ckeditor.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/ckeditor/adapters/jquery.js")" type="text/javascript"></script>
<h2 class="title">
<span class="text-cms">CM<span>S</span></span></h2>
<div class="box">
<div class="t">
</div>
<div class="c">
<div class="content">
<div class="main-holder">
<div id="sidebar">
<ul>
<li>Home</li>
<li>Pages</li>
</ul>
</div>
<div id="content" style="min-height: 500px;">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Page Type - #Html.DropDownListFor(x => x.PageTypeID, Model.PageTypeList)
#Html.ValidationMessageFor(model => model.PageTypeID)</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title, new { #class = "text-box" })
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="clear">
</div>
<div class="editor-label">
#Html.LabelFor(model => model.HtmlContent)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.HtmlContent, new { #name = "Editor1", #class = "Editor1" })
#Html.ValidationMessageFor(model => model.HtmlContent)
</div>
<div class="clear">
</div>
<p>
<input type="submit" value="Save" class="input-btn" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
</div>
</div>
<div class="b">
</div>
</div>