I am creating an ASP.NET MVC 3 application which references a database and within the Edit.cshtml file I wish to limit the edit function of one of the items (Score) to simply be either 0,1,2,3,4. I have tried the options in this post Converting HTML.EditorFor into a drop down (html.dropdownfor?) but cannot seem to get it working.
<div class="editor-label">
#Html.LabelFor(model => model.Score)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Score, "YesNoDropDown")
#Html.ValidationMessageFor(model => model.Score)
</div>
Many thanks.
Chri3
If you don't forsee it needing to be changed often, I'd just use a static select like below:
<div class="editor-label">
#Html.LabelFor(model => model.Score)
</div>
<div class="editor-field">
<select id="score" name="score">
#for(int i=0; i<5; i++){
<option value="#i" #(Model.Score==i ? " selected": "")>#i</option>
}
</select>
</div>
to use Html.DropDownListFor, you need to pass in the argument of type IEnumerable<SelectListItem>.
Here i've created some mockup for you
in Model
public class SampleModel
{
public string SeletectRateItem { get; set; }
public IList<SelectListItem> RateItem
{
get
{
var item = new List<SelectListItem>();
item.Add(new SelectListItem(){Selected = false, Text = "1", Value = "1"});
item.Add(new SelectListItem(){Selected = false, Text = "2", Value = "2"});
item.Add(new SelectListItem(){Selected = false, Text = "3", Value = "3"});
item.Add(new SelectListItem(){Selected = false, Text = "4", Value = "4"});
item.Add(new SelectListItem(){Selected = false, Text = "5", Value = "5"});
return item;
}
}
}
and in cshtml
#Html.DropDownListFor(m => m.SeletectRateItem, Model.RateItem)
Related
I have implemented kendo combobox on my MVC5 view and want the combobox to be filtered based on the value in my model. I need to retrieve the value from my model. I have currently bound that value to a hidden field called CountryCode in my partial view. The script is in my main view. I am getting error message undefined while trying to access the hidden field. The model is definitely getting populated with the CountryCode.
#using System.Collections
#model CC.GRP.MCRequest.ViewModels.RequestStatusUpdateViewModel
#{
Layout = null;
}
<div class="k-popup-edit-form k-window-content k-content" >
<div class="k-edit-form-container">
#Html.HiddenFor(model => model.CountryCode)
<div class="editor-label">
#Html.LabelFor(model => model.RequestID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RequestID, new { htmlAttributes = new { #readonly = "readonly" } })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProjectName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProjectName, new { htmlAttributes = new { #readonly = "readonly" } })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RequestStatus)
</div>
<div class="editor-field">
#(Html.Kendo().ComboBoxFor(model => model.RequestStatusCode)
.HtmlAttributes(new { style = "width:100%" })
.DataTextField("Status")
.Placeholder("Select...")
.DataValueField("RequestStatusCode")
.AutoBind(false)
.Filter("contains")
.DataSource(dataSource => dataSource
.Read(read =>
{
read.Action("GetRequestStatus", "Request")
.Type(HttpVerbs.Post)
.Data("GetCountryCodeFilter");
}).ServerFiltering(true)
)
)
</div>
#Html.ValidationMessageFor(model => model.RequestStatusCode, "", new { #class = "text-danger" })
</div>
</div>
Controller method that populates the combo
public ActionResult GetRequestStatus(string countryCode)
{
var response = requestRepository.GetRequestStatus(countryCode).AsQueryable().ProjectTo<RequestStatusViewModel>();
var jsonResult = Json(response, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
Controller method that loads the view
public ActionResult RequestStatus(int requestId, string projectName, string countryCode)
{
RequestStatusUpdateViewModel reqeustStatusUpdateViewModel = new RequestStatusUpdateViewModel();
reqeustStatusUpdateViewModel.RequestID = requestId;
reqeustStatusUpdateViewModel.ProjectName = projectName;
reqeustStatusUpdateViewModel.CountryCode = countryCode;
if (!ModelState.IsValid)
{
// return View("NewRequestView", Mapper.Map<RequestStatusViewModel>(newReqeustViewModel));
return null;
}
return View("_RequestStatusView", Mapper.Map<RequestStatusUpdateViewModel>(reqeustStatusUpdateViewModel));
}
RequestStatusViewModel
public class RequestStatusViewModel
{
public string RequestStatusCode { get; set; }
public string Status { get; set; }
public int DisplaySequenceNo { get; set; }
}
Script in the main view
function GetCountryCodeFilter() {
alert("Hello");
alert($('#CountryCode').val());
return { countryCode: $('#CountryCode').val() }
};
I'm assuming this is a popup from a kendo grid. The problem is that view gets serialized and sent to the popup when any row is clicked. It doesn't bind the data like you expect in MVC - it sends the same serialized data every time. See here.
So change your hidden to use kendo's MVVM binding so each instance gets the value from the grid row. (CountryCode needs to exist in the grid's datasource):
#Html.HiddenFor(x => x.CountryCode, new { data_bind = "value: CountryCode" }) // underscore becomes dash
Or you could just use:
<input type="hidden" name="CountryCode" id="CountryCode" data-bind = "value: CountryCode"/>
I need to populate two textbox controls based on a selection of combo box in my MVC 5 application. The combo box is a kendo MVC control. The values that need to be assigned to the text box controls are there in the collection that is bound to the combo box control. Could somebody let me know how to go about it. Does this need to be handled in javascript/ jquery or is it handled in kendo combo box events. An example would be great.
Combo box
#Html.LabelFor(model => model.Name1, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-4">
<div class="editor-field">
#(Html.Kendo().ComboBoxFor(model => model.Name1)
.HtmlAttributes(new { style = "width:300px" })
.DataTextField("Name1")
.DataValueField("CustomerMasterDataId")
.DataSource(dataSource => dataSource
.Read(read => read.Action("RequestHeader_CustomerData", "Request").Type(HttpVerbs.Post))
)
)
</div>
#Html.ValidationMessageFor(model => model.CustomerNumber, "", new { #class = "text-danger" })
</div>
Textbox
<div class="form-group">
#Html.LabelFor(model => model.CustomerNumber, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-6">
<div class="editor-field">
#Html.EditorFor(model => model.CustomerNumber, new { htmlAttributes = new { #class = "form-control" } })
</div>
#Html.ValidationMessageFor(model => model.CustomerNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="clearfix"></div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerGroup, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-6">
<div class="editor-field">
#Html.EditorFor(model => model.CustomerGroup, new { htmlAttributes = new { #class = "form-control" } })
</div>
#Html.ValidationMessageFor(model => model.CustomerGroup, "", new { #class = "text-danger" })
</div>
</div>
Controller method that poulates the combo
public ActionResult RequestHeader_CustomerData()
{
var response = requestRepository.GetCustomerData().AsQueryable().ProjectTo<CustomerViewModel>();
var jsonResult = Json(response, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
Please note that the CustomerNumber and Name1 field would be used to populate the text boxes
ViewModel
public class CustomerViewModel
{
public int CustomerMasterDataId { get; set; }
public int CustomerNumber { get; set; }
[Display(Name = "Customer Name")]
public string Name1 { get; set; }
[Display(Name = "Customer Group")]
public string CustomerGroup { get; set; }
}
Yes, handle the change event:
#(Html.Kendo().ComboBoxFor(model => model.Name1)
.HtmlAttributes(new { style = "width:300px" })
.DataTextField("Name1")
.DataValueField("CustomerMasterDataId")
.DataSource(dataSource => dataSource
.Events(e => e.Change(onComboChange))
.Read(read => read.Action("RequestHeader_CustomerData", "Request").Type(HttpVerbs.Post))
)
)
Now handle it in js:
#section scripts
{
<script type="text/javascript">
function onComboChange(e) {
var dataItem = e.sender.dataItem();
if (dataItem) {
//set the value of text box (input)
$("#CustomerNumber").val(dataItem.CustomerNumber);
$("#CustomerGroup").val(dataItem.CustomerGroup);
};
};
</script>
}
Here is a js equivalent: http://jsfiddle.net/sg53719/74LwhebL/1/
So I have been using MVC for a while but not a lot of Ajax.
So the issue i have is that I have a create view where the first field of the create view is a Date picker which on change i want ajax to change the selection dropdown of another field on the form. I have the Ajax update working , but the form Post (Create button), now only calls into the Ajax method on the controller. I want the Ajax post back to call the default method Create method on the Controller. So the Create form contains 3 fields
A date (which has the OnChange Ajax submit)
A drop down list of id's and text
other fields as required
I have included the model and cshtml view files (one the partial). The controller is just simply a method taking either the datetime value or the entire model.
So I have the code working where, when the date changes it updates the relevant LocalIds field, but because the 'create' button is inside the Ajax.BeginForm tags, when the create button is pressed, it generates an Ajax call, and I want it to generate a Form Post. What am i missing
CreateModel
public class IdsSelection
{
public string Id { get; set; }
public List<SelectListItem> Selections { get; set; }
}
public class CreateModel
{
[Display(Name="Local Id's")]
public IdsSelection LocalIds;
[Display(Name="Day")]
[Required, DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}",ApplyFormatInEditMode=true)]
public DateTime Date { get; set; }
[Required, Display(Name = "Other Value")]
[Range(1.0, 1000000.0, ErrorMessage ="Value must be greater than zero")]
public decimal OtherValue { get; set; }
public CreateModel()
{
Date = DateTime.Today;
}
}
CreateView.cshtml
#Model Demo.Models.CreateModel
....
#using (Ajax.BeginForm("ChangeDate", "Process", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
UpdateTargetId = "changeid",
}))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new
{
htmlAttributes = new
{
autofocus = "autofocus",
#class = "form-control",
#Value = Model.Date.ToString("yyyy-MM-dd"),
onchange = "$(this.form).submit();"
}
})
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
#Html.Partial("_ShowIds", Model.LocalIds)
<div class="form-group">
#Html.LabelFor(model => model.OtherValue, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OtherValue, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OtherValue, "", 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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
}
_ShowIds.cshtml
#model Demo.Models.IdsSelection
<div id="changeid" class="form-group">
#Html.Label("Local Id", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#if(Model.Selections.Count == 1) // one
{
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(model => model.Selections[0].Text, new
{
htmlAttributes = new
{
#class = "form-control",
#readonly = "readonly",
}
})
}
else // more entries so show a dropdown
{
#Html.DropDownListFor(model => model.Id, Model.Selections, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model, "", new { #class = "text-danger" })
}
</div>
</div>
You have not shown you controller methods, but assuming the method which generates this view is named Create(), then create a corresponding POST method
[httpPost]
public ActionResult Create(CreateModel model)
for saving the data. The remove #using (Ajax.BeginForm("ChangeDate", "Process", new AjaxOptions() { ..... ])) and replace with a normal form to post back to the server (and you can remove jquery.unobtrusive-ajax.min.js)
#using (Html.BeginForm())
{
....
Next create a controller method that returns your partial view based on the selected date
public PartialViewResult FetchLocalIds(DateTime date)
{
IdsSelection model = // populate model based on selected date
return PartialView("_ShowIds", model);
}
Next, in the view, wrap the current #Html.Partial() in a placeholder element
<div id="localids">
#Html.Partial("_ShowIds", Model.LocalIds)
</div>
Then use jquery to handle the datepickers .change() event and call the FetchLocalIds() method to update the DOM using the .load() function.
$('#Date').change(function() {
$('#localids').load('#Url.Action("FetchLocalIds")', { date: $(this).val() })
})
Note also to remove onchange = "$(this.form).submit();" from the #Html.EditorFor(model => model.Date, new { ... })
I have MVC3 project whit claim based authentication.
I've managed to get the sign in to work.
What I want to do now is to fill a DropDownList with with groups, then another DropDownlist with users belonging to this group.
As of now, I fill the DropDownlist with users which are from my local database.
This my method for getting the users from a certain office
private static void GetUsers(UsersContext.Format wFormat)
{
new UsersContext(new Uri("http://site/api/users"), wFormat)
.Users
.Where(x => x.Office.Equals("HomeOffice"))
.ToList()
.ForEach(
item =>
{
var user = item.DisplayName;
});
}
This how my controller get filled now:
public ActionResult Create()
{
var model = new TaskViewModel();
var subjectypes = createNKIRep.GetAllSubjectTypesById();
var customer = createNKIRep.GetAllCustomersByID();
var teams = createNKIRep.GetAllTeamsByID();
var users = createNKIRep.GetAllUsersByID();
model.Teams = new SelectList(teams, "Id", "Name");
model.Users = new SelectList(users, "Id", "Name");
model.SubjectTypes = new SelectList(subjectypes, "Id", "Name");
model.Company = new SelectList(customer, "Id", "CompanyName");
}
return View(model);
This my view where I fill the dropdownlist
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<h4>Get users to customer</h4>
<legend></legend>
<p>Subjecttype</p>
<div class="editor-field">
#Html.DropDownListFor(m => m.SubjectTypeName, Model.SubjectTypes, "Subjecttype", new { #class = "selectstyle" })
#Html.ValidationMessageFor(model => model.SubjectTypeName)
</div>
<p>Team</p>
<div class="editor-field">
#Html.DropDownListFor(model => model.TeamName, Model.Teams, "Team", new { #class = "selectstyle"})
#Html.ValidationMessageFor(model => model.TeamName)
</div>
<p>Users</p>
<div class="editor-field">
#Html.DropDownListFor(model => model.UserName, Model.Users, "Users", new { #class = "selectstyle"})
#Html.ValidationMessageFor(model => model.UsersName)
</div>
<p>Customer</p>
<div class="editor-field">
#Html.DropDownListFor(model => model.CompanyName, Model.Company, "Company", new { #class = "selectstyle" })
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
So my problem is, that I don't know how to fill the Dropdownlist with the values from my AD using restful service. Any idea how it can be done?
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