Validation for dropdown list in MVC - asp.net-mvc

I am new to MVC and I am still trying to explore stuffs inside MVC. Now I came to place where I need to implement validation for drop-down list.
I spent fair amount of time searching for validation in MVC. But I dint get any result which suits to my problem. Please bear in mind that I am new to MVC so correct me if I my approach is wrong for implementing validation.
I created model class like this :
public class ValidationModel
{
[Required(ErrorMessage = "Please select atleast one option")]
public string dropdown { get; set; }
}
And In my controller I am using this code :
#using (Html.BeginForm("selectIndex", "Home", FormMethod.Post))
{
<select name ="dropdwnMode" id="dropdwnMode" class="textbox">
<option selected="selected" value="">Select Mode
#Html.ValidationMessageFor(Model => Model.dropdown) </option>
<option value="1"> Add or Update Customer </option>
<option value="2">Update Customer Credit</option>
</select>
}
Here what I want is when my drop-down list is in Select Mode that is default, it should show please select at least one option error and if user selects 1st or 2nd option then this error message should not display.
But Now it is not displaying any message if I use above approach. So can someone guide me in achieving this?

I would recommend replacing your "Dropdown" property on the model with two properties - one that would hold the selected value and one that would hold all possible values. Then you can use an HTML helper (Razor syntax) to create your dropdown in the view, and MVC would take care of the validation.
Model:
public class ValidationModel
{
/// <summary>
/// ID of the selected option
/// </summary>
[Required(ErrorMessage = "Please select at least one option")]
public int SelectedOptionID { get; set; }
/// <summary>
/// Possible values for the dropdown
/// </summary>
public IEnumerable<OptionModel> Options { get; set; }
}
OptionModel:
public class OptionModel
{
/// <summary>
/// ID (key that uniquely identifies this option)
/// </summary>
public int ID { get; set; }
/// <summary>
/// Name (value or display text that will be shown in the UI)
/// </summary>
public string Name { get; set; }
}
You could also store other properties on the option model if needed. Alternatively, you could use a key-value-pair or dictionary instead of the option model to avoid creating a class, which would be faster but less clear.
View:
#model ValidationModel
#using (Html.BeginForm("selectIndex", "Home", FormMethod.Post))
{
#Html.ValidationMessageFor(Model => Model.SelectedOptionID)
#Html.DropDownListFor(m => m.SelectedOptionID,
new SelectList(Model.Options, "ID", "Name"),
"Select an option") // Default text before the user has selected an option
}
You could name Options and SelectedOptionID a bit better to clarify their usage depending on your implementation.
With this implementation you would (and should) populate the options list from your controller. The selected option would be populated when the user selects an option. Like this:
Controller:
public ActionResult YourAction()
{
ValidationModel model = new ValidationModel();
model.Options = new List<OptionModel> {
new OptionModel { ID = 1, Value = "Add or Update Customer" },
new OptionModel { ID = 2, Value = "Update Customer Credit" }
}
return View("YourViewName", model);
}
As a side note, I would recommend naming your ValidationModel after what the view that uses it is doing (e.g. HomepageModel if your view is the entire homepage or NavigationFormModel if your view is just a partial view that contains a dropdown for navigation).

Related

How to display Enum as DropDownList in .Net MVC Core

I have a class called Options where I'm keeping a bunch of enumerables for different options. One of these is a Sizes enumerable which is used in my Ball model. How do I get my Size field for my Ball to display as a dropdown list when creating a new Ball? I assume I need to make an Editor Template but I don't know what that's supposed to look like.
Here's some example code:
Options.cs
public class Options
{
public enum Sizes
{
Small,
Medium,
Large
};
public enum Material
{
Rubber,
Plastic,
Metal
};
}
Ball.cs
public class Ball
{
public string Name { get; set; }
public Options.Sizes Size { get; set; }
public Options.Material Material { get; set; }
}
Index.cshtml
#model WebApplication.Models.Ball
<form asp-action="Create" asp-controller="Ball">
#Html.EditorForModel()
<input type="submit" value="Submit"/>
</form>
How do I get EditorForModel to display the enum properties as DropDownLists with the possible values of the Enum?
I figured it out with a bit of help from this source
I didn't create a helper like it suggests but I used the same principles from the helper to create a EditorTemplate for my Sizes enum. Here's what it looks like:
Sizes.cshtml
#model Options.Sizes
#{
var values = Enum.GetValues(typeof(Options.Sizes)).Cast<Options.Sizes>();
IEnumerable<SelectListItem> items =
from value in values
select new SelectListItem
{
Text = value.ToString(),
Value = value.ToString(),
Selected = (value.Equals(Options.Sizes.ATX_Full))
};
}
#Html.DropDownList("Size", items)
Now when I call #Html.EditorForModel() in my Ball.cshtml, it references this editor template and creates a dropdown with all the options in the enum.

Tempdata In Razor

learning MVC creating a simple date time signup form. i am trying to make two textboxes for the user. One for date and one for time.
I am going to save both values in a single dateTime field in my model.
So i need to figure out how to have a form field stored in tempdata to be accessed in the controller when posted. i can then combine the two text boxes to make a dateTime to store in my model.
I know how to get the tempdata in the controller, its just the razor syntax in the form i cant quite get.
Thanks in advance.
TempData is not fit for your purpose. You can not assign value in TempData/ViewData in a View. You can assign value in TempData/ViewData in controller only and access those value in View. For more information please refer this question: TempData moving from view to controler
I suggest you to use a ViewModel in your scenario having all properties what you need in your view. Please look into answer given by Mariusz at ASP.NET MVC - How exactly to use View Models
I think a better practice would be to have a view model containing all the information you want to pass from the form to the controller.
View Model:
public class MyViewModel
{
public string Username { get; set; }
public string Password { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
Controller:
public ActionResult Signup()
{
var m = new MyViewModel();
return View(m);
}
[HttpPost]
public ActionResult Signup(MyViewModel m)
{
var username = m.Username;
var password = m.Password;
var date = m.Date;
var time = m.Time;
// ...
}
View:
#model MvcApplication5.Controllers.MyViewModel
#* ... *#
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Username)
#Html.PasswordFor(m => m.Password)
#Html.TextBoxFor(m => m.Date)
#Html.TextBoxFor(m => m.Time)
<input type="submit" value="Submit" />
}

ASP.NET MVC: Getting values from TextBoxFor, TextBox, DropDownList and TextArea

In ASP.Net MVC I am opening one view from another view. The first view sends two values to the second view. In the second view the user can send an email.
I am having two problems.
The first problem is that the two values that I send from the first view aren't being shown in my second view.
The second problem is that I can't get the email form to trigger my email function in the controller.
Here's a more detailed explanation.
My first view named ViewOne is using the controller ControllerOne. In ViewOne I have the following code to call the second view, ViewTwo:
#Html.ActionLink("Go to second view", "ViewTwo", "Home", new { firstval = firstval, secondval = secondval }, null)
When the ActionLink is clicked, the following function in the controller HomeController is called:
public ActionResult ViewTwo(string firstval, string secondval)
{
MyModel model = new MyModel();
model.firstval = firstval;
model.secondval = secondval;
var list = new SelectList(new[]
{
new {ID="1",Name="One"},
new{ID="2",Name="Two"},
new{ID="3",Name="Three"},
},
"ID", "Name", 1);
model.myList = list;
return View(model);
}
So in the controller HomeController I attempt to populate the model myModel with the values I get from the first view, ViewOne.
The model MyModel looks like this:
public class MyModel
{
public string firstval { get; set; }
public string secondval { get; set; }
public IEnumerable<SelectListItem> myList { get; set; }
[Required]
[DisplayName("My name")]
public string reporter { get; set; }
[Required]
[DisplayName("Description")]
public string description { get; set; }
[DisplayName("Dropdown")]
public string myDropDownListValue { get; set; }
}
The view ViewTwo looks like this:
#model myapp.Models.MyModel
#{ ViewBag.Title = "Send e-mail"; }
<hgroup class="title">
<h1>#ViewBag.Title</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Send e-mail</legend>
<p>First value:</p>
<p>#Html.LabelFor(m => m.firstval)</p>
<p>Second value:</p>
<p>#Html.LabelFor(m => m.secondval)</p>
<p>Reporter</p>
<p>#Html.TextBoxFor(m => m.reporter)</p>
<p>Dropdownlist</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>Description:</p>
<p>#Html.TextAreaFor(m => m.description, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send e-mail"/>
</fieldset>
}
In the controller HomeController, which is the same controller that has the ViewTwo() function that gets triggered right before the above form gets drawn, I have the following function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult sendEmail(ContactModel model) // (string keyword, string partofspeech, string reporter, string category, string description, string acceptance)
{
// code to send email
}
So I want this function, sendEmail, to get triggered whenever I submit the form. But that doesn't happen. What happens when I click the submit button (labeled "Send e-mail") is that the view ViewTwo gets reloaded and the ActionResult ViewTwo() in the controller HomeController gets triggered. This is my second (and biggest) problem.
Also, my first problem is that
<p>#Html.LabelFor(m => m.firstval)</p>
Doesn't show the value that gets sent from the first view. It shows the string "firstval". Before the form is drawn I can see in the function ViewTwo() that the value gets correctly sent from the first view.
Any ideas?
EDIT:
Second problem solved. See my reply below.
You have a few options, normally with a postback you would submit the form with an <input type="submit" value="sendEmail" />, the values in the form would be represented in a ViewModel like:
public class EmailFormViewModel()
{
public string value1 {get; set;}
public string reporter {get; set;}
//More properties on the form
}
Your endpoint would look like this:
[HttpPost]
public ActionResult SendEmail(EmailFormViewModel model)
{
//Send the email
}
If you still want to use a hyperlink to submit the form, which natively performs a GET request, you will can catch the click with javascript, and manually send the form via Ajax.
Something like:
$('#sendEmail').click(function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
data: $('#formId').serialize(),
url: '/controllerName/sendemail'
}).done(function(response) {
//Do something on success response
});
});
Update:
You should also decorate your post action sendEmail with [ValidateAntiForgeryToken] and add a #Html.AntiForgeryToken() within the form. This will help protect against cross site forgery requests.
You can build your form, endpoint and model like this:
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<p>#Html.LabelFor(m => m.value1)</p>
<p>#Html.EditorFor(m => m.value1)</p>
<p>#Html.LabelFor(m => m.reporter)</p>
<p>#Html.EditorFor(m => m.reporter)</p>
<p>#Html.LabelFor(m => m.myDropDownListValue)</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>#Html.LabelFor(m => m.myTextAreaValue)</p>
<p>#Html.TextAreaFor(m => m.myTextAreaValue, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send Email"/>
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendEmail(myModel model)
{
//Send the email
}
public class myModel
{
public IEnumerable<SelectListItem> myList { get; set; }
[DisplayName('Value 1')]
public string value1 { get; set; }
[DisplayName('Reporter')]
public string reporter { get; set; }
[DisplayName('Text Area')]
public string myTextAreaValue { get; set; }
[DisplayName('Dropdown')]
public string myDropDownListValue { get; set; }
}
As long as you are already on the same controller, it will postback to /controllername/sendemail with the form data inside the post. You should also look up attributes on your models, you can enforce descriptions and validations for example. Check here for more details, its MVC 2 but still relevant.
If you really want to be able to GET the values instead of POSTing them, change the form's action to GET and change the target to be sendEmail
Remove the ActionLink and replace it with a simple submit button
I know you said you wanted to keep the ActionLink, but this will achieve the same thing
I managed to solve my first problem. Once I specified which controller the function sendEmail is in, that code finally got triggered. Like so:
#using (Html.BeginForm("sendEmail", "Home"))
Now if I can only figure out why
<p>#Html.LabelFor(m => m.firstval)</p>
isn't working then I'm home safe.
It actually prints out the string "firstval" instead of taking the value of the string variable firstval that I set in the model. (See my first post for more detailed explanation).
EDIT:
I fixed that last problem. The very werid thing is that the above code with LabelFor doesn't work. But if I do this instead:
<p>#Model.firstval</p>
then I get the value. But it doesn't get sent back to the controller when the form is submitted. But that I solved with:
#Html.HiddenFor(m => m.firstval)
So HiddenFor works for me, LabelFor doesn't.
Case closed. I'm throwing the "solved checkmark" to the guy who gave me all that help here above. He did awesome. But the real solution is in this post. :)

Partial editor to show single property of model

I have my model as follows
public class PlaceOrder
{
public int orderCode { set; get; }
public string Order_ID { set; get; }
public int orderDetailCode { set; get; }
[Required]
public string Topic { set; get; }
//50 more fields are there
}
Using editorforModel displays all the fields in the model. I want to have a editor helper which takes the property name and only shows editor for that specific property.
I wrote a create/edit/details actions for my model and working fine. What my final goals is that I want to have edit button next to every field on the details view. As soon I click on edit it allows to update and validate the input as well
EDIT
I am using following snippet for edit link
#(Html.Awe().PopupFormActionLink()
.LinkText("Edit")
.Name("editP")
.Url(Url.Action("PropertyEdit", "PlaceOrder", new
{
PropertyName = Html.NameFor(model => model.SubjectCategoryCode),
propertyValue = Html.IdFor(model => model.SubjectCategoryCode),
ordercode = Model.orderCode
})
)
.Title("Editor for " + Html.NameFor(model => model.SubjectCategoryCode))
and I want something that I pass the field name and it dispalys the relevant fields and do the validation
You could just use an EditorFor and a form for each field:
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.ordercode)
<input type="submit" />
}
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.orderDetailCode)
<input type="submit" />
}
Of course, you would need a different action for each item and you need a way to get the other values as well, since you're only posting one value to the controller. To achieve this you could include a hidden field with the id and retrieve the other values on the server.
There's the Html.EditorFor(m => m.Property) method for this (your model should be set to PlaceOrder to use this helper, as with any statically typed helpers).
Edit: Bah, Kenneth was faster :-).

Binding to a SelectList in MVC

Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.
Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.
But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.
Here's the simplified code:
For the model:
public class DemoCreateViewModel
{
public SelectList Choice { get; set; }
}
For the controller:
//
// GET: /Demo/Create
public ActionResult Create()
{
DemoCreateViewModel data = new DemoCreateViewModel();
data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
ViewData.Model = data;
return View();
}
//
// POST: /Demo/Create
[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And for the View:
<fieldset>
<legend>Fields</legend>
<%= Html.LabelFor(model => model.Choice) %>
<%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?
Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.
Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:
public class DemoCreateViewModel
{
public string Choice { get; set; }
public SelectList Choices { get; set; }
}
And then the DropDownListFor statement in the View becomes:
<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>
When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.
Here is one approach:
#Html.DropDownListFor(model => model.Choice,
ViewBag.Choices as SelectList,
"-- Select an option--",
new { #class = "editor-textbox" })
Notice that I use ViewBag to contain my SelectList. This way when you post back, the client doesn't send the entire select list up to the server as part of the model.
In your controller code, you just need to set the view bag:
ViewBag.Choices = new SelectList(....
Consider creating a different view model for your post action without the SelectList property:
public class DemoCreateViewModelForUpdate
{
public string Choice { get; set; }
}
Then you can always map from the DemoCreateViewModelPost instance to an DemoCreateViewModel instance if the model state is invalid and you want to re-show the view. I tend to prefer everything needed by the view to be in my display view model class, so using a separate update only view model let's me keep things slim and trim for the trip back to the server.
In your view, you'd do:
#Html.DropDownListFor(m => m.Choice, Model.Choices)
as in the previous answer, so no unnecessary data would round trip.

Resources