I have an EmailFormModel class.
public class EmailFormModel
{
[Required, Display(Name = "Your Name:")]
public string FromName { get; set; }
[Required, Display(Name = "Your Email:")]
public string FromEmail { get; set; }
[Required, Display(Name = "To Email:")]
public string ToEmail { get; set; }
public List<SelectListItem> CCEmail { get; set; }
[Required]
[AllowHtml]
public string Message { get; set; }
public EmailFormModel()
{
CCEmail = new List<SelectListItem>();
}
}
Now I need this email to have multiple CC recipients, hence why I made the property CCEmail a type of List. In my HttpGet method I am populating the list which is correctly working. In my HttpPost I am doing this:
foreach(var item in model.CCEmail)
{
message.CC.Add(new MailAddress(item.Text));
}
Now, in my View... what can I do to display these email addresses.. so that when I hit Submit they will be submitted as email addresses?
Currently in my View I have this:
<div class="form-group">
#Html.LabelFor(m => m.CCEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.ListBoxFor(m => m.CCEmail,null, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.CCEmail)
</div>
</div>
Is there another/better way to display the email addresses rather than ListBoxFor?
But when I select the email addresses.. and then hit Submit, I get an error message:
The value 'John.Doe#test.com,Test.User1#test.com' is invalid.
Those aren't the real email addresses.. the ones that I am using are valid.
Any help is appreciated.
Even though I have found an alternative solution, I am still looking for a cleaner solution. Here is what I have done.
I changed the CCEmail property to a List<string>.
So, in the HttpPost method I changed the foreach loop to this syntax:
foreach(var item in model.CCEmail)
{
message.CC.Add(new MailAddress(item));
}
Then in my view, I did this:
<div class="form-group">
#foreach(var item in Model.CCEmail)
{
#Html.LabelFor(m => m.CCEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBox("CCEmail", item, htmlAttributes: new { #class = "form-control", #readonly = true } )
</div>
}
</div>
Even though this creates 2 separate textboxes, it still submits as 2 separate email addresses instead of both of them combined as what I think the error in my OP was.
Again, if you know of a simpler/cleaner solution, please post!
Related
I have created form to add customer. I rendered customer page with Viewmodel. View Model class as follows,
public class CustomerViewModel
{
public IEnumerable<MemberShipType> MemberShipTypes { get; set; }
public Customer Customers { get; set; }
}
public class Customer
{
[Display(Name ="Customer ID")]
public int CustomerId { get; set; }
[Required(ErrorMessage = "Please enter customer name")]
[StringLength(255)]
[Display(Name ="Customer Name")]
public string CustomerName { get; set; }
public MemberShipType MemberShipType { get; set; }
[Required(ErrorMessage = "Please select membership type")]
[Display(Name = "Membership Type")]
public byte MembershipTypeId { get; set; }
}
public class MemberShipType
{
[Display(Name ="Membership Id")]
public byte Id { get; set; }
[Required]
[Display(Name = "Subscription Plan")]
public string Name { get; set; }
}
After adding that class, we have created Action to save customer form data using a single model class(Not viewModel)
I have created Customer form using Viewmodel to display with Membership type data.
UI is rendering fine with the below code. But, I am not able to get the model data in the action method.
If I directly use the viewmodel in the action data is coming fine. The problem needs to map all the view model property to a particular model.
It's required more time to map model property each time.
Can any know how to directly use entity framework add method with customer Model(Not View model)
#using (Html.BeginForm("Save", "Customer", FormMethod.Post))
{
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.Customers.CustomerName, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => m.Customers.CustomerName,
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Customers.CustomerName, "",
new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Customers.MembershipTypeId, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-lg-10">
#Html.DropDownListFor(m => m.Customers.MembershipTypeId,
new SelectList(Model.MemberShipTypes, "Id", "Name"),
"Please Select", new {#class = "form-control"})
#Html.ValidationMessageFor(m => m.Customers.MembershipTypeId,
"",
new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<input type="reset" value="Reset" class="btn btn-default" />
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</div>
}
The below action model always return null.
[System.Web.Mvc.HttpPost]
public ActionResult Save(Customer customer)
{
if (customer.CustomerId == 0)
{
_context.Customer.Add(customer);
_context.SaveChanges();
}
}
I am getting a Customer model is null. If I pass customerViewModel data is coming. Can anyone know the answer on how to directly get the data in the model class?
Since you're binding the view to a model of CustomerViewModel and you're using the HTML helpers EditorFor (lambda overload), you should expect that same model in return on your POST. When you use LabelFor and EditorFor, the automatic naming will probably give you something like "Customers_CustomerName" so it can put your view model back together again.
One solution is to change your expected model on your save method to be a 'CustomerViewModel' and just use the .Customer property to get the data.
[System.Web.Mvc.HttpPost]
public ActionResult Save(CustomerViewModel model)
{
if (model.Customer.CustomerId == 0)
{
_context.Customer.Add(model.Customer);
_context.SaveChanges();
}
}
Another option is to name your input fields manually to reflect properties of the 'Customer' model directly and it will map them into a "Customer" model for you on POST. eg Instead of #Html.LabelFor(m => m.Customers.CustomerName you'd just use #Html.EditorFor("CustomerName", Model.Customers.CustomerName)
<div class="form-group">
#Html.LabelFor(m => m.Customers.CustomerName, htmlAttributes:
new { #class = "control-label col-md-2" })
<div class="col-md-10">
*********EDIT HERE --> #Html.TextBox("CustomerName", Model.Customers.CustomerName
new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Customers.CustomerName, "",
new { #class = "text-danger" })
</div>
</div>
I got the solution for this issue. The reason for the problem is, I've created controller and Model in the same name. So, I've changed Model name with different alias in Viewmodel like below,
public class CustomerViewModel
{
public IEnumerable<MemberShipType> MemberShipTypes
{ get; set; }
public ProductionCustomer productionCustomers
{ get; set; }
}
If we use model object in controller to Get/POST it will work even if we rendered the form with ViewModel(Multiple Model). By default mvc will identify the model to post in the form.
I got a model like this:
public class SignUpModel
{
[Required, DisplayName(#"Particulars/Salutation")]
public short Salutation { get; set; }
public IEnumerable<SalutationOption> SalutationOptions { get; set; }
[Required, DisplayName("Particulars/FirstName")]
public string FirstName { get; set; }
[Required, Display(Name = "Particulars/LastName")]
public string LastName { get; set; }
[Required, DataType(DataType.EmailAddress), Display(Name = "Particulars/Email")]
public string Email { get; set; }
[Required, DataType(DataType.EmailAddress), Compare("Email"), Display(Name = "Particulars/EmailConfirmation")]
public string EmailConfirmation { get; set; }
}
Now in my view, I want to post validation-errors (also client-side) using #Html.ValidationMessageFor.
<div class="section-field">
<div class="form-group">
#Html.LabelFor(model => model.Email, new { #class = "control-label required" })
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { autocomplete = "off" } })
#Html.ValidationMessageFor(x=>x.Email);
</div>
</div>
<div class="section-field">
<div class="form-group">
#Html.LabelFor(model => model.EmailConfirmation, new { #class = "control-label required" })
#Html.EditorFor(model => model.EmailConfirmation, new { htmlAttributes = new { autocomplete = "off" } })
#Html.ValidationMessageFor(x=>x.EmailConfirmation);
</div>
</div>
But in Html.ValidationMessageFor I only want to show things like "invalid email-adress" or "both emails must match".
I don't want to show the message saying, that the fields are required, since all are required - and they get bordered red.
Is it possible somehow? Tried with implementing a custom HtmlHelper, but that doesn't filter anything client-side.
So I fixed it simply with css now.
I add a class required-validation to invalid Required-validations:
<p class="validation-error required-validation">The field 'FirstName' is required</p>
and added into my custom.css
p.validation-error.required-validation {
display: none;
}
Other ideas on how to handle this with ASP are welcome :)
I have a form with a submit button that should pass through the item to the actionlistener. I thought it might be similar to the question in #Html.HiddenFor does not work on Lists in ASP.NET MVC but none of the answers seem to work. You can even see my for-loop taken from one of the answers in there.
[
EDIT: I have gotten rid of the mass of hidden loops and replaced with #Html.EditorFor so that you can see, even if not hidden, the flags list does not get to the actionlistener. This is a problem because when someone edits the flags, there is no way to update the db as I cannot get the ID of the flag updated.
]
The ModelState in the controller is never valid, regardless whether I keep the "[Bind(Include =" there or not. That's just there because of the tutorial for
ASP.NET MVC Tutorial: Web application development with Azure Cosmos DB.
ItemController.cs:
[HttpPost]
[ActionName("ProductEdit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditProductAsync( [Bind(Include = "Id, Name, Flags")] Item model)
{
Item product = await DocDBRepo<Item>.GetItem(model.Id);
model.Organisations = product.Organisations;
if (ModelState.IsValid) //Checks item validation via "required" set on properties
{
await DocDBRepo<Item>.UpdateItemAsync(model.Id, model);
return RedirectToAction("Index");
}
return View(model);
}
[HttpGet]
[ActionName("ProductEdit")]
public async Task<ActionResult> EditProductAsync(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Item item = await DocDBRepo<Item>.GetItem(id);
if (item == null)
{
return HttpNotFound();
}
return View(item);
}
ProductEdit.cs:
#model RRPortal.Models.Item
#{
ViewBag.Title = "ProductEdit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ProductEdit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Flags, htmlAttributes: new { #class = "control-label col-md-2 " })
</div>
#*Flags list*#
#for (int i = 0; i < Model.Flags.Count; i++) //foreach (var flag in Model.Flags)
{
<div class="form-group">
//#Html.HiddenFor(modelItem => Model.Flags[i].Id)
#Html.Label(Model.Flags[i].Name, htmlAttributes: new { #class = "control-label col-md-3" })
#Html.LabelFor(modelItem => Model.Flags[i].Enabled, htmlAttributes: new { #class = "control-label col-md-1" })
<div class="col-md-8">
#Html.EditorFor(modelItem => Model.Flags[i].Enabled, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(modelItem => Model.Flags[i].Enabled, "", new { #class = "text-danger" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Item.cs:
public class Item
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[Required]
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "flags")]
public List<Flag> Flags { get; set; }
[JsonProperty(PropertyName = "organisations")]
public List<Organisation> Organisations { get; set; }
}
public class Flag
{
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[Required]
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[Required]
[JsonProperty(PropertyName = "enabled")]
public bool Enabled { get; set; }
}
public class Organisation
{
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "users")]
[Display(Name ="Users")]
public List<User> UserStore { get; set; }
}
public class User
{
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[Required]
[JsonProperty(PropertyName = "fname")]
public string FName { get; set; }
[Required]
[JsonProperty(PropertyName = "lname")]
public string LName { get; set; }
[Required]
[Display(Name = "Admin?")]
[JsonProperty(PropertyName = "isadmin")]
public bool IsAdmin { get; set; }
}
The Item's Id and Name comes through and is not null when I debug the controller, but the Flags List is always empty. The ModelState shows the following exception: {"The parameter conversion from type 'System.String' to type 'RRPortal.Models.Flag' failed because no type converter can convert between these types."}
I have also been asked where the ModelState is showing the exception so below is a screenshot:
I will gladly update the question if anyone has any questions. I have been tweaking the view for 2 days now and still can't get the item to contain anything. The rendered HTML appears to contain the organisation and inner objects perfectly fine.
Any help is appreciated!
My guess is that in your HttpGet view you have something along the lines of:
[HttpGet]
public ActionResult EditProductAsync()
{
var model = new ProductViewModel()
{
Flags = _uow.Products.GetFlags(),
Organisations = _uow.Products.GetOrganisations()
};
return View(model);
}
Because these objects are not also returned as part of your form, they are returning to the server as empty which is throwing an error for you, thus invalidating the model. Before you check if the model is valid, you should first do something like this:
[HttpPost]
[ActionName("ProductEdit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditProductAsync( [Bind(Include = "Id, Name, Flags, Organisations")] Item model)
{
model.Organisations = _uow.Products.GetOrganisations();
model.Flags = _uow.Products.GetFlags();
if (ModelState.IsValid)
{
await DocDBRepo<Item>.UpdateItemAsync(model.Id, model);
return RedirectToAction("Index");
}
return View(model);
}
By populating those fields, any model errors you have are strictly your client's errors on submitting the form.
This question already has answers here:
Pass List of Checkboxes into View and Pull out IEnumerable [duplicate]
(2 answers)
Closed 6 years ago.
I need to populate a checkbox list of equipment on a form for users to request equipment. The data for the list is stored in a table named 'Equipment'. I am working with EF 6 database first. The view is strongly typed and will write to an 'Orders' table. I am stuck on how to use a View Model and not ViewBag to populate the check box list for the form. I have looked at MikesDotNetting, the Rachel Lappel post about view models and several others and it's not making sense to me.
Code below:
public class Equipment
{
public int Id { get; set; }
public string Description { get; set; }
public string Method { get; set; }
public bool Checked { get; set; }
}
public class Order
{
public int id{ get; set; }
public string Contact_Name { get; set; }
public List<Equipment>Equipments { get; set; }
public string Notes { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Contact_Name,Equipment,Notes")] Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(order);
}
View
#model CheckBoxList.Models.Order
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Order</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Contact_Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Contact_Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Contact_Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
//checkbox list populated here
</div>
<div class="form-group">
#Html.LabelFor(model => model.Notes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Notes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Notes, "", new { #class = "text-danger" })
</div>
</div>
See this answer for how to do it How to bind checkbox values to a list of ints?
That example uses Guid's as PK's but that can be easily replaced with int's.
I'm going to assume your Equipment class is your EF entity.
So you are creating your order page so let's start with the CreateOrderViewModel
public class CreateOrderViewModel
{
public string Contact_Name { get; set; }
public Dictionary<int, string> AllEquipment{ get; set; }
public int[] SelectedEquipment { get;set; }
public string Notes { get; set; }
}
Populate AllEquipment with just the id and the name of the piece of equipment. This is the complete list of equipment that will be needed to show all the equipment checkboxes with the value of the id of the equipment.
Something like
var viewModel = new CreateOrderViewModel {
AllEquipment = context.Equipment.ToDictionary(e => e.Id, e.Description);
}
SelectedEquipment is the list of equipment with checkboxes checked. So when you post this information back, the SelectedEquipment property will have a list of all the id's that need to be attached to the order.
When you create the order just iterate through the list and add them to the Equipment list in your Order entity.
Make a for loop in your list and generate a checkbox for every item in it.
<div class="form-group">
#for (int i = 0; i < Model.Equipments.Count(); i++)
{
#Html.CheckBoxFor(x => x.Equipments[i].Checked)
#Model.Equipments[i].Description
//If you need to hide any values and get them in your post
#Html.HiddenFor(x => x.Equipments[i].Id)
#Html.HiddenFor(x => x.Equipments[i].Method)
}
</div>
I'm very new to MVC 5 and web programming in general so please bear with me.
I have a view (used to manage user roles) where I have three separate forms, which I more or less copied and pasted from a tutorial. In the tutorial the fields for the forms were created in the following way:
Username : #Html.TextBox("Username")
Since I wanted the styling to work for them, I changed the code to look more like the default forms in the MVC 5 template, so it ended up looking like this:
#Html.LabelFor(m => m.GetRolesUsername, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.GetRolesUsername, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.GetRolesUsername, "", new { #class = "text-danger" })
</div>
My model ManageUserRolesViewModel looks like this (note that at the top of my view I have #model ManageUserRolesViewModel):
public class ManageUserRolesViewModel
{
#region Assign Role
[Required]
[Display(Name = "Username", ResourceType = typeof(Resources))]
public string AssignRoleUsername { get; set; }
[Required]
[Display(Name = "RoleName", ResourceType = typeof(Resources))]
public string AssignRoleRole { get; set; }
#endregion
#region Get Roles
[Required]
[Display(Name = "Username", ResourceType = typeof(Resources))]
public string GetRolesUsername { get; set; }
#endregion
#region Unassign Role
[Required]
[Display(Name = "Username", ResourceType = typeof(Resources))]
public string UnassignRoleUsername { get; set; }
[Required]
[Display(Name = "RoleName", ResourceType = typeof(Resources))]
public string UnassignRoleRole { get; set; }
#endregion
}
Notice how I'm using annotations to load the name of the elements in the ViewModel directly from resources. I'm doing this for localization purposes, and the resources are returning strings in Spanish. I think this may be the root of my issue, but I'm not sure.
Then, in my controller I have the following method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult GetRoles(string UserName)
{
if (!string.IsNullOrWhiteSpace(UserName))
{
ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
ViewBag.RolesForThisUser = this.UserManager.GetRoles(user.Id);
ViewBag.Roles = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
}
return View("ManageUserRoles");
}
Now, here's what happens: if I use Username : #Html.TextBox("Username"), when the method GetRoles() gets called in the controler, the UserName parameter is there and the user is successfully loaded. If instead I use the
#Html.LabelFor(m => m.GetRolesUsername, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.GetRolesUsername, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.GetRolesUsername, "", new { #class = "text-danger" })
</div>
when the method gets called, the UserName parameter is null.
My wild guess is that somewhere in the code MVC is looking for UserName or Username and finding Usuario instead, but I am not sure if this is true and in any case, I'd like to know how to solve the issue.
Assuming your form has the model defined as:
#model ManageUserRolesViewModel
and somewhere in the view:
#Html.TextBoxFor(m => m.GetRolesUsername, new { #class = "form-control" })
The action should look like this:
[HttpPost]
public ActionResult GetRoles(ManageUserRolesViewModel vm)
{
string userName = vm.GetRolesUsername;
//rest of code omitted.
}
So you do not rely on the UserName parameter and can use the view model itself.
if this does not suffice, you could do this:
#Html.TextBoxFor(x => x.GetRolesUsername, new { Name = "UserName" })
Hi Eric,
#Html.TextBox("Username")
// It creates a html input element with name "Username"
<input type="text" id=""Username" name="Username" value="" />
And
#Html.TextBoxFor(m => m.GetRolesUsername)
// It also create a html element with name as it's property name. ie, "GetRolesUsername"
<input type="text" id=""Username" name="Username" value="#Model.GetRolesUsername" />
So, if you submit your form, your browser will send parameters as,
Username = "value in the #html.TeaxtBox()",
GetRolesUsername = "value in the #html.TextBoxFor()"
So, both values will be passed to your MVC controller. Then you can decide what parameters you want to receive.
public ActionResult Submit(string Username, string GetRolesUsername)
{
// You can get Username and GetRolesUsername here
}
public ActionResult Submit(string Username)
{
// You tell your controller that I am expecting only one parameter and that is Username
}
Then there will be a main difference between `#html.TextBox()` and `#html.TextBoxFor()' is,
`#html.TextBox()` will just create a text element. But, `#html.TextBoxFor()' will create and set value to the element.
Hope this will help you.
If you have any doubts, please feel free to ask me.
**And Grand Welcome To MVC.**
I am also just a beginner in MVC :)