Hard coding dropdown list on the view page - c#

I have a string input type and I want it to remain string. But I want the user to chose from a list of options when he inputs the information. So I want to add a drop down list but I don't know how to do it. I don't want to create any model or any list in the back end ... I want to be able to do this on the view only.
Here is my initial code:
<div class="form-group">
#Html.LabelFor(model => model.EmployeeType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EmployeeType, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EmployeeType, "", new { #class = "text-danger" })
</div>
</div>

If you do not want to use any viewmodels/Html helper methods for rendering your dropdown, you may simply use pure Html. Just keep names for your form elements and use that as parameters of your HttpPost action method.
#using(Html.Beginform())
{
<input type="text" name="userName"/>
<select name="state">
<option value="MI">Michigan<option>
<option value="OH">Ohio<option>
</select>
}
And your HttpPost action would be
[HttpPost]
public ActionResult Register (string userName,string state)
{
//check the parameter values now
// to do : Save and redirect.
}
If you are already having a ViewModel object as parameter for your HttpPost action method, You can add the SELECT element name as second param and it will still work.
[HttpPost]
public ActionResult Register (CreateUserVM model,string state)
{
//check the parameter values now
// to do : Save and redirect.
}
I am not quite sure about your exact use case, If you have more than 1 or 2 form elements you are posting, I recommend using a view model and rely on MVC Model binding. Adding the dropdown data to the viewmodel is not that hard.
public class CreateUserVM
{
public string UserName {set;get;}
public List<SelectListItem> EmployeeTypes {set;get;}
public int SelectedType {set;get;}
}
And in your GET action method, Load the EmployeeTypes collection and send it to the view
public ActionResult Create()
{
var vm= new CreateUserVM();
vm.EmployeeTypes = GetEmployeeTypes();
return View(vm);
}
public List<SelectListItem> GetEmployeeTypes()
{
return new List<SelectListItem>()
{
new SelectListItem
{
Value = "1",
Text = "PERM"
},
new SelectListItem
{
Value = "2",
Text = "Temporary"
}
};
}
And in your view,
#model CreateUserVM
#using(Html.BeginForm())
{
#Html.TextBoxFor(v=>v.UserName)
#Html.DropdownListFor(s=>sSelectedType,Model.EmployeeTypes,"Select one")
}
And in your HttpPost action, Read the SelectedType property to get the selected Item's value.
[HttpPost]
public ActionResult Register (CreateUserVM model)
{
//check the model.SelectedType
// to do : Save and redirect.
}
If you do not want to hard code the Employee Types in the server side code, Create a db table and store it there and read it in your GET action method. This will allow you to add a new employee type to the system without touching the code.

Take a look at this example: http://www.asp.net/mvc/overview/older-versions/working-with-the-dropdownlist-box-and-jquery/using-the-dropdownlist-helper-with-aspnet-mvc
Here is some basic code to create a dropdownlist in a view:
#{
var listitems = new List<SelectListItem>();
listitems.Add(new SelectListItem() { Text = "One", Value="1"});
listitems.Add(new SelectListItem() { Text = "Two", Value="2"});
}
#Html.DropDownList("DropDownListValue", listitems)

I'm not sure why you would want to fill the data in the View, but you want to use:
#Html.DropDownList
So your code would be something along the lines of....
<div class="form-group">
#Html.LabelFor(model => model.EmployeeType, "Employee Type", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("EmployeeType", new SelectList(new List<string> { ... }, "EmployeeType", "SelectListName"), htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.EmployeeType, "", new { #class = "text-danger" })
</div>
</div>
Obviously you need to actually put your strings into that list some how. Personally I would create or get the list in the controller and pass it in a ViewBag. Then you could just...
#Html.DropDownList("EmployeeType", new SelectList(ViewBag.EmployeeTypes, "EmployeeType", "SelectListName"), htmlAttributes: new { #class = "form-control" })

Related

ResetPassword feature doesn't use code from link

I have an old MVC website I'm looking at. I don't understand the following code in my Account controller. (I believe this was code generated by ASP.NET.)
Controller
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
View
#model OnBoard101.Models.ResetPasswordViewModel
#{
ViewBag.Title = "Reset password";
}
<h2>#ViewBag.Title</h2>
#using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Reset your password</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Code)
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-success" value="Reset" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
These methods are called when the user uses the Forgot Password feature and then clicks the link that is sent to them in an email.
As best I can tell, the POST handler correctly detects an invalid code in the link (it produces an Invalid Token error when the code is not value). But I don't understand how. The GET handler seems to simply discard the code argument.
I don't get how this code works. How does model.Code ever get populated?
The ResetPassword feature does use code from the link.
Model binding retrieves data from various sources such as route data, form fields, and query strings.
It inspects the query string parameters for matching properties on the model used by the view
While it may appear like the code is being discarded by the Controller GET action, the code is still a part of the request and used by the view.
And since the view explicitly binds to a model
#model OnBoard101.Models.ResetPasswordViewModel
which has a matching public Code property (case-insensitive)
public string Code { get; set; }
it will bind it to the model in the view during the GET and then use it (the model) to populate a hidden form field (as shown in your View markup)
#Html.HiddenFor(model => model.Code)
So now when the form is submitted, the POST action will bind that field to the model posted back to the action and perform the validation
The same could have been achieved with
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code) {
if (code == null) return View("Error");
var model = new ResetPasswordViewModel {
Code = code
};
return View(model);
}
But since the built-in model binding functionality would initialize a model if one was not provided, the above code really does not add anything additional to what the framework does out-of-the-box.

ASP.NET, MVC ListBoxFor Edit "The parameter 'expression' must evaluate to an IEnumerable when multiple selection is allowed."

I am receiving an error that I cannot figure out and even previously answered questions are not binding the ListBox the same way I am.
Here is how I use the ListBox on my create view:
ASP.NET MVC Return Comma Delimited String From From ListBoxFor To Controller
Now when I go to my edit I receive this error:
The parameter 'expression' must evaluate to an IEnumerable when
multiple selection is allowed.
On this code:
#Html.ListBoxFor(model => model.Mask_Concat, Enumerable.Empty<SelectListItem>(), new { #class = "chosen-container chosen-container-multi", #style = "width:300px" })
I have tried everything I can think of, binding the ListBox to a ViewModel, still no luck.
Model:
public string Mask_Concat { get; set; }
View:
<div class="fancy-form" id="mainMask">
#Html.LabelFor(model => model.Mask_Concat, "Mask(s)", new { #class = "control-label col-md-2" })
<div class="col-md-12">
#Html.ListBoxFor(model => model.Mask_Concat, Enumerable.Empty<SelectListItem>(), new { #class = "chosen-container chosen-container-multi", #style = "width:300px" })
#Html.ValidationMessageFor(model => model.Mask_Concat, "", new { #class = "text-danger" })
</div>
</div>
<input type="hidden" id="selectedMaskValues" name="selectedMaskValues" />
JS on view:
//join selected mask values
$("#Mask_Concat").chosen().change(function () {
var $hidden = $("#selectedMaskValues");
$hidden.val($(this).find('option:selected').map(function () {
return $(this).val();
}).get().join(","));
});
Controller:
public ActionResult Edit(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Chip_Master chipMaster = db.Chip_Master.Find(id);
if (chipMaster == null)
{
return HttpNotFound();
}
ViewBag.Manufacturer = new SelectList(db.Chip_Master, "Mask_Concat", "Mask_Concat", chipMaster.Mask_Concat.Split(',').ToList());
return View(chipMaster);
}
Database:
Mask_Concat Column: 1234,5678,2345,7890
Thoughts? Need more information?
ListBoxFor is used to display a collection of values. You're trying to map it to a single string value. I would add a property to your model that splits Mask_Concat into an array and bind that to the list box:
public string[] Mask_Concat_Values
{
get
{
return Mask_Concat.Split(',');
}
set
{
Mask_Concat = string.Join(",",values);
}
}

Properly handling model validation errors

How do I get this simple form validation going?
I have an AccountVerificationController which originally had the following method:
public ActionResult Index(AccountVerificationModel model)
Problem is when view is initially loaded, there are validation errors since the model has required fields as follows:
public class AccountVerificationModel
{
[Required]
public string MerchantId { get; set; }
[Required]
public string AccountNumber { get; set; }
[Required, StringLength(9, MinimumLength = 9, ErrorMessage = "The Routing Number must be 9 digits")]
}
However, this is not desired behavior. I want validation to occur only after user clicks on a validate button, so I changed form to invoke the Verify method in the account controller.
View is as follows;
#using (Html.BeginForm("Verify", "AccountVerification", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>#ViewBag.Title</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.SubMerchantId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MerchantId, new { htmlAttributes = new { #class = "form-control", placeholder = "MID" } })
#Html.ValidationMessageFor(model => model.MerchantId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RoutingNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RoutingNumber, new { htmlAttributes = new { #class = "form-control", placeholder = "9 Digit Routing Number" } })
#Html.ValidationMessageFor(model => model.RoutingNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input name="validate" type="submit" value="Validate" class="btn btn-info"/>
</div>
</div>
</div>
}
Now the challenge is handling model validation errors. I have the controller structured as follows:
public class AccountVerificationController : BaseMvcController
{
public AccountVerificationController()
{
}
public ActionResult Index()
{
return View(new AccountVerificationModel());
}
public ActionResult Verify(AccountVerificationModel model)
{
// do the validation then re-direct......
if (!model.IsValid())
{
return RedirectToAction("Index", model);
}
// otherwise try to validate the account
if (!model.VerificationSuccessful)
{
// repopulate the view with this model...
return RedirectToAction("Index", model);
}
return Redirect("Index");
}
However, during re-direction, I am losing the entire context, model errors and all. Reading upon this whole model binding but if someone can quickly spot what I am doing wrong here, that would be appreciated.
Well, you can't redirect when there's errors. Period. Redirection is actually a two-step process, even though it seems to happen seemlessly. First, the server returns a 301 or 302 status code with a header value indicating the URL the client should go to instead. Then, the client issues a brand new request to that given URL. That's why no context is persisted. It's basically the same as the client requesting the page for the very first time.
The only way to maintain the context is to simply return a view. Which means your Verify action would need to return the same view as your Index action. However, that's not exactly ideal. I don't exactly understand what the original problem was that made you decide to add the Verify action in the first place, but that can likely be fixed if you open a new question to that regard. You should really just postback to your Index action.

MVC UI for ASP.NET CheckboxFor Issue

I have a big problem with MVC Telerik UI for ASP.NET
I am trying to get a checkbox up for a boolean field. I know we have two input fields to return the value false when the box is not touched.
When I do not touch the CBox, I get the value 'false' as expected. When I check the box, I get false too because the CBOx is returning a string = "true,false" which makes it impossible to convert directly to bool.
View
public class role
{
public string role_name { get; set; }
public bool add_school { get; set; }
}
Controller
public ActionResult test()
{
return View();
}
[HttpPost]
public async Task<ActionResult> test(Models.role role)
{
var z = Request["cb_addschool"];
var x = 1;
return RedirectToAction("Index");
}
View
#model Models.role
#using (Html.BeginForm("test", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h2>Add a New Role</h2>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.role_name, new { #class = "col-md-1 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.role_name, new { #class = "form-control form-control-big" })
</div>
</div>
<div class="form-group">
#Html.Kendo().CheckBoxFor(m=>m.add_school).Name("cb_addschool").Label("Add School")
</div>
<div class="form-group">
<div class="col-md-offset-3 col-md-9">
<input type="submit" class="btn btn-login" value="Register" />
</div>
</div>
}
Please, any help?
Remove this code from your action method:
var z = Request["cb_addschool"];
You have this value inside your role model. So this is pointless in this case.
Than remove this attribute from Kendo CheckBoxFor:
.Name("cb_addschool")
You don't have to need that (the property will be bound correctly without that).
Small hint: if you are using Kendo - use the Kendo().TextBoxFor method instead of #Html.TextBoxFor (or add "k-textbox" class to your TextBoxFor - it will use Kendo CSS styles).
Here is an example:
#(Html.Kendo().TextBoxFor(model => model.role_name)
.HtmlAttributes(new { placeholder = "Select role", #class = "form-control form-control-big" })
)

html.dropdownlistfor not posting back to controller in form

I have a form in an MVC application. The post back returns the model with all values populated except for the value of a drop down. This is the code I wrote for the drop down, where exactly am I going wrong?
Model:
public class ViewModel : Model
{
public Dictionary<int, string> ServiceFocusList { get; set; }
public int ServiceFocus { get; set; }
}
View:
#using
(Html.BeginForm("SaveServiceRequest","ServiceRequest",FormMethod.Post))
{
var AddLotLink = Url.Action("SaveServiceRequest", "ServiceRequest");
var labelCol = "control-label col-md-4";
#Html.LabelFor(model => model.ServiceFocusList, new { #class = #labelCol })
#Html.ValidationMessageFor(model => model.ServiceFocusList)
#Html.DropDownListFor(model => model.ServiceFocusList ,new SelectList(Model.ServiceFocusList,"Key",
"Value", Model.ServiceFocus), "Select a Service Focus")
<input type="submit" id="AddLotBtn" value="Add A Lot" class="saveDraft btn btn-default" urllink="#AddLotLink" />
}
Controller:
public ActionResult SaveServiceRequest(ViewModel model, long? lotId)
{
//At this point both model.ServiceFocusList and model.ServiceFocus are null
ServiceRequestSupport.SaveServiceRequest(model);
RedirectToAction("CreateLotOffSR", "Lot", new {area = "Lot", model.ID});
}
The first parameter of DropDownListFor is supposed to identify the property containing the selected value. You want this:
#Html.DropDownListFor(model => model.ServiceFocus, new SelectList(Model.ServiceFocusList,"Key",
"Value", Model.ServiceFocus), "Select a Service Focus")
Change your DropdownList in a view to following, you were binding it to dictionary instead of property that holds selected value.
#Html.DropDownListFor(model => model.ServiceFocus ,new SelectList(Model.ServiceFocusList,"Key",
"Value", Model.ServiceFocus), "Select a Service Focus")

Categories

Resources