How can I prevent EditorTemplate logic from overriding View logic? - c#

Using MVC5 I've added view logic to disable data entry based on a value in the model. After the logic runs it is overridden by existing EditorTemplate logic - the field remains enabled. How can I make my disable logic work? This view logic runs first:
<div>
#{
object attributes = new { #class = "form-control", #disabled = "disabled", #readonly = "readonly" };
if (Model.OnHold.Number == 0) {
attributes = new { #class = "form-control datePicker" };
}
#Html.EditorFor(model => model.OnHold.DateIssued, attributes);
}
</div>
Then the conflicting EditorTemplate code:
#model DateTime
#Html.TextBox(string.Empty, #Model.ToString("MM/dd/yyyy"), new { #class = "form-control datepicker" })

When you call #Html.EditorFor(model => model.Property, attributesObj) and you have a custom editor view defined, you can see that the intellisense for the function says that the htmlAttributes object is also included in the ViewData of the view. In other words, if you access the ViewData property within your editor view, you should have access to the properties you're passing from your #Html.EditorFor(model => model.OnHold.DateIssued, attributes); statement. Here's a simple example:
// Index.cshtml - assuming model.Message is of type string
#Html.EditorFor(model => model.Message, new { #class = "text-danger" })
// EditorTemplates/String.cshtml
#model string
#{
object textboxAttributes = new { #class = "text-success" };
// Override the default #class if ViewData property contains that key
if (ViewData.ContainsKey("class") && string.IsNotNullOrWhitespace(ViewData["class"].ToString())
{
textboxAttributes = new { #class = ViewData["class"] };
}
}
#Html.TextboxFor(model => model, textboxAttributes)
In your case your EditorTemplate will now look like this:
#model DateTime
#{
object dateAttributes = new { #class = "form-control datepicker" };
if (ViewData.ContainsKey("disabled") && !String.IsNullOrWhiteSpace(ViewData["class"].ToString()))
{
dateAttributes = new { #class = ViewData["class"], #readonly = "readonly", #disabled = "disabled" };
}
}
#Html.TextBox(string.Empty, #Model.ToString("MM/dd/yyyy"), dateAttributes)

Related

System.InvalidOperationException on saving

When I fill in the information on my add product page and try to save, I get the error.
public ActionResult Create()
{
List<SelectListItem> values= (from i in db.Categories.ToList()
select new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString()
}
).ToList();
ViewBag.val = values;
return View();
}
View
<div class="form-group">
#Html.LabelFor(model => model.CategoryId, "CategoryId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m=> m.CategoryId ,(List<SelectListItem>)ViewBag.val, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CategoryId, "", new { #class = "text-danger" })
</div>
</div>
Error
System.InvalidOperationException: 'The ViewData item that has the key 'CategoryId' is of type 'System.Int32' but must be of type 'IEnumerable'.'
ViewBag doesn't require type-casting, try changing your code in View from
#Html.DropDownListFor(m=> m.CategoryId ,(List)ViewBag.val, new { #class = "form-control" })
to
#Html.DropDownListFor(m=> m.CategoryId ,#ViewBag.val, new { #class = "form-control" })
Or, you may also refer this SO answer
and use something like below -
#Html.DropDownListFor(m => m.ContribType,
new SelectList(#ViewBag.ContribTypeOptions, "ContribId",
"Value", Model.ContribTypeOptions.First().ContribId),
"Select, please")

Set selected drop down value from database in razor view MVC

I am explicitly creating a dropdownlist in razor mvc view. I would like the default option to be one of the items, the value of that item should come from database
View Code
<div class="form-group">
#Html.LabelFor(model => model[i].Customer.HomeType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="SerivcesRegistrationInput col-md-10">
#Html.DropDownListFor(model => model[i].Customer.HomeType, new List<SelectListItem>
{
new SelectListItem{ Text="House", Value = "House",#(Model[i].CustomerServices.HomeType == "House" ? Selected = true : false) },
new SelectListItem{ Text="Apartment", Value = "Apartment" },
new SelectListItem{ Text="Farm", Value = "Farm" } },
"Select your home type", htmlAttributes: new { #class = "form-control" })
}
</div>
</div>
Controller Code
cs.HomeType = serviceDetails.HomeType;
sp.CustomerServices = cs;
return View("EditCustomerServices", ProviderList);
Model has the data I need but how can I set it to correct Dropdown list value. I tried using short hand if but it gives me an error
Invalid member initializer
Can I add if condition for Selected Boolean?
#{
var selected = Model.CustomerServices.HomeType == "House";
}
<div class="form-group">
#Html.LabelFor(model => model.Customer.HomeType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="SerivcesRegistrationInput col-md-10">
#Html.DropDownListFor(model => model.Customer.HomeType, new List<SelectListItem>
{
new SelectListItem{ Text="House", Value = "House", Selected = selected },
new SelectListItem{ Text="Apartment", Value = "Apartment" },
new SelectListItem{ Text="Farm", Value = "Farm" } },
"Select your home type", htmlAttributes: new { #class = "form-control" })
}
</div>
</div>

How to set selected item in dropdownlist using LINQ?

Currently my code is selecting the first item in the list. I want it to select the item which matches the MakeModelTypeCode.
E.G.
The selected item in the dropdownlist should be where this code
vehicleViewModel.VehicleDTO.VehicleDetails.MakeModelTypeCode
= MakeModelTypeCode from x
Here is the relevant code from the Business Logic class:
vehicleViewModel.AvailableMakeModels = GetAllMakeModelTypesForClient(selectedClientId);
vehicleViewModel.AvailableMakeModels = vehicleViewModel.AvailableMakeModels.GroupBy(x => x.ModelDescription).Select(x => x.First()).Distinct().ToList();
var vehicleMakeList = vehicleViewModel.AvailableMakeModels
.Select(s =>
new SelectListItem
{
Selected = true,
Text = s.MakeDescription,
Value = s.MakeModelTypeCode
});
Here is the relevant code from the .cshtml:
<div class="col-sm-6">
#Html.LabelFor(m => m.VehicleDTO.VehicleDetails.MakeDescription)
#Html.DropDownListFor(x => x.SelectedvendorText, new SelectList(Model.AvailableMakesSelectList, "Value", "Text", "Make"), new { #class = "form-control uppercase", #id = "ddlAvailableMakes", name = "ddlAvailableMakes", #onchange = "FillModels()" })
#Html.ValidationMessageFor(m => m.SelectedMake, "", new { #class = "text-danger" })
</div>
Here is the code from the Controller:
[Route("Edit-{id}")]
[Authorize]
public ActionResult Edit(string id)
{
VehicleViewModel vehicleViewModel = new VehicleViewModel();
selectedClientId = HelperMethods.GetClientId();
vehicleViewModel.VehicleDTO = this.vehicleBusinessLogic.GetClientVehicleDTO(id, this.selectedClientId);
vehicleViewModel = this.vehicleBusinessLogic.SetUpUpdateVehicle(vehicleViewModel, selectedClientId);
vehicleViewModel.VehicleDTO.VehicleDetails.ClientID = this.selectedClientId;
return View(vehicleViewModel);
}
I have tried a few different ways but can't get any to work. I can get just the item I want returned but not all the items + the selected item.
I was thinking I could do a nested Where clause inside the Select but that doesn't seem to work, but maybe my syntax was in-correct.
How about
MakeModelTypeCode toBeSelected;
var vehicleMakeList = vehicleViewModel.AvailableMakeModels
.Select(s =>
new SelectListItem
{
Selected = s.MakeModelTypeCode == toBeSelected,
Text = s.MakeDescription,
Value = s.MakeModelTypeCode
});
Changing the .cshtml to the below resolved the issue.
The only change was to replace x => x.SelectedvendorText with m => m.VehicleDTO.VehicleDetails.MakeModelTypeCode
<div class="col-sm-6">
#Html.LabelFor(m => m.VehicleDTO.VehicleDetails.MakeDescription)
#Html.DropDownListFor(m => m.VehicleDTO.VehicleDetails.MakeModelTypeCode, new SelectList(Model.AvailableMakesSelectList, "Value", "Text", "Make"), new { #class = "form-control uppercase", #id = "ddlAvailableMakes", name = "ddlAvailableMakes", #onchange = "FillModels()" })
#Html.ValidationMessageFor(m => m.SelectedMake, "", new { #class = "text-danger" })
</div>

How to keep the id from the selectlist when using multiple columns in the textfield

I am trying to show multiple columns from my database in a dropdownlist using a SelectList
public ActionResult Create()
{
var times = db.Show_Courses
.Select(x =>
new {
id = x.show_course_id,
times = x.show_course_before + "," + x.show_course_after
});
ViewBag.course_id = new SelectList(times, "id", "times");
return View();
}
It shows up properly in the view but in the database the value of the id is 0
This was my code before i wanted to show two columns in the textfield of the selectlist
public ActionResult Create()
{
ViewBag.course_id = new SelectList(db.Show_Courses, "show_course_id", "show_course_time_before");
return View();
}
And here is my code in the view
<div class="form-group">
#Html.LabelFor(model => model.course_show_id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("course show id", (SelectList)ViewBag.course_id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.course_show_id, "", new { #class = "text-danger" })
</div>
</div>
So my question is how can I display 2 values in the Textfield of the Selectlist without the id becoming 0?
You could generate SelectList manually.
For example,
ViewBag.course_id = db.Show_Courses
.Select(x => new SelectListItem {
Text = x.show_course_before + "," + x.show_course_after,
Value = x.show_course_id.ToString() })
.ToList();
I personally like to use strongly type model. You can read more here
Your code seems to be fine, you need to map the dropdown to according value from model: show_course_id - from what I see, so please update your dropdown to:
#Html.DropDownList("show_course_id", (SelectList)ViewBag.course_id, new { htmlAttributes = new { #class = "form-control" } })

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key AnnouncementId

I am trying to do a table that updates with a dropdown list and a box to input text but keep getting that error in the title. It is directly copied from the create cshtml.
Index.cshtml:
#model Announcements1.Models.Comment
<div id="CommentDiv"></div>
<div id="CommentCreateForm">
#using (Ajax.BeginForm("AjaxCreate", "Comments",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "CommentDiv"
}))
{
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CommentContent, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CommentContent, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CommentContent, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AnnouncementId, "AnnouncementId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AnnouncementId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.AnnouncementId, "", new { #class = "text-danger" })
</div>
</div>
}
</div>
#section Scripts {
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Custom_Scripts/BuildCommentTable.js"></script>
#Scripts.Render("~/bundles/jqueryval")
}
CommentController:
public ActionResult BuildCommentTable()
{
var comments = db.Comments.Include(c => c.Announcement);
return PartialView("_CommentTable", GetMyComments());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AjaxCreate([Bind(Include = "CommentId,CommentContent,AnnouncementId")] Comment comment)
{
if (ModelState.IsValid)
{
db.Comments.Add(comment);
db.SaveChanges();
}
ViewBag.AnnouncementId = new SelectList(db.Announcements, "AnnouncementId", "AnnouncementContent", comment.AnnouncementId);
return PartialView("_CommentTable", db.Comments.Include(c => c.Announcement));
}
and my JS file:
$(document).ready(function () {
$.ajax({
url: '/Comments/BuildCommentTable',
success: function (result) {
$('#CommentDiv').html(result);
}
});
});
Firstly,you are missing the code to populate the Annoucements DropDownList data in the BuildCommentTable action, add it before returning the View as you did in the Post action.
Secondly that name the ViewBag key same as your Model property, your both actions should have the ViewBag populationo like:
ViewBag.AnnouncementOptions= new SelectList(db.Announcements, "AnnouncementId", "AnnouncementContent", comment.AnnouncementId);
so your get action would be like:
public ActionResult BuildCommentTable()
{
ViewBag.AnnouncementOptions= new SelectList(db.Announcements, "AnnouncementId", "AnnouncementContent");
var comments = db.Comments.Include(c => c.Announcement);
return PartialView("_CommentTable", GetMyComments());
}
Same way change the post action method ViewBag population code part also:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AjaxCreate([Bind(Include = "CommentId,CommentContent,AnnouncementId")] Comment comment)
{
if (ModelState.IsValid)
{
db.Comments.Add(comment);
db.SaveChanges();
}
ViewBag.AnnouncementOptions = new SelectList(db.Announcements, "AnnouncementId", "AnnouncementContent", comment.AnnouncementId);
return PartialView("_CommentTable", db.Comments.Include(c => c.Announcement));
}
and now in your view, you should be able to use strongly typed helper method DropDownListFor this way:
<div class="col-md-10">
#Html.DropDownListFor(x=>x.AnnouncementId, ViewBag.AnnouncementOptions as SelectList, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.AnnouncementId, "", new { #class = "text-danger" })
</div>
Hope it helps you.

Categories