submit button doesn't pass input from textbox - asp.net-mvc

Basically i have an input box in which an user can type in his email, and a button that submits the email. I can press the button, and it redirects to my "details" page. However, the input from the texbox is not passed to my controller.
View:
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div class="form-group form-inline">
<label class="margin20">Sign up for newsletter</label>
#Html.TextBoxFor(Model => Model.Email, new { name= "mail", Class = "form-control", Style = "display:inline-block; max-width:200px", Placeholder="Example#Example.com" })
<input type="submit" class="btn btn-default" style="display:inline-block" id="emailSignup"/>
</div>
}
Controllers
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string mail)
{
return RedirectToAction("details", new {address = mail });
}
public ActionResult details(string address)
{
EmailSignup person = new EmailSignup { Email = address};
return View(person);
}
}
i left the model out, because it basically is 1 property.

Your
#Html.TextBoxFor(Model => Model.Email, ...)
is generating an input with name="Email".
Note that new { name = "mail" } does absolutely nothing fortunately (look at the html your generating) because if it did, it would screw up the model binding process - the whole purpose of using the HtmlHelper methods is to bind to your model.
You could change the method to
[HttpPost]
public ActionResult Index(string email)
and the parameter will be correctly bound, however your method should be
[HttpPost]
public ActionResult Index(XXX model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return RedirectToAction("details", new { address = model.Email });
}
where XXX is the model that you declared in the view (i.e. with #model XXX), so that you get correct model binding and can take into account validation.
Note also that you property should be
[Display(Name = "Sign up for newsletter")]
[Required("Please ...")] // assuming you want to ensure a value is submitted
[EmailAddress] // assuming you want a valid email
public string Email { get; set; }
and then the view will be
#Html.LabelFor(m => m.Email) / correctly generates a label associated with the input
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", placeholder="Example#Example.com" })
#Html.ValidationMessageFOr(m => m.Email)
and I recommend adding another class name and using css rather than you inline style = ".." element

Related

MVC model validation

So, im currently building an application that needs the user model validating, and if the incorrect properties are filled in to the user it will tell them.
I have the data annotations set up, but im not sure how i relay the error message back to the user?
I have this set up so far on my model and view.
Model
public class DatabaseModel
{
[Required(ErrorMessage = ("A first name is required"))]
public string FirstName { get; set; }
[Required(ErrorMessage = ("A last name is required"))]
public string LastName { get; set; }
[Required(ErrorMessage = ("A valid role is required"))]
public string Role { get; set; }
// TODO - Validate rank to only b 1 - 10
//
[Range(1,10, ErrorMessage = ("A rank between 1 and 10 is required"))]
public int Rank { get; set; }
}
And View
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.Label("First Name")
<br>
#Html.TextBoxFor(m => m.FirstName)
<br>
#Html.Label("Last Name")
<br>
#Html.TextBoxFor(m=>m.LastName)
<br>
#Html.Label("Role")
<br>
#Html.TextBoxFor(m => m.Role)
<br>
#Html.Label("Rank")
<br>
#Html.TextBoxFor(m => m.Rank)
<br><br>
<input type="submit" value="Save">
}
My Controller
public class HomeController : Controller
{
// GET: Home
[HttpGet]
public ActionResult Index()
{
DatabaseModel model = new DatabaseModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View();
}
}
I believe the model needs to be passed back to the view in order to display the error message, and although i have read through the documentation on asp.net i cannot understand how they just add the error message and the form knows how to display the errors to the user.
I am extremely confused.
You need to use ModelState.IsValid in your Controller and also #Html.ValidationMessageFor(model => model.FirstName) in your view:
public ActionResult Index(ViewModel _Model)
{
// Checking whether the Form posted is valid one.
if(ModelState.IsValid)
{
// your model is valid here.
// perform any actions you need to, like database actions,
// and/or redirecting to other controllers and actions.
}
else
{
// redirect to same action
return View(_Model);
}
}
For your example:
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.LabelFor(m=>m.FirstName)
<br>
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
<br>
#Html.LabelFor(m=>m.LastName)
<br>
#Html.TextBoxFor(m=>m.LastName)
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
. . .
<input type="submit" value="Save">
}
Controller:
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View(model);
}

Model binding doesn't work for complex object

Here's the view I'm going to post:
#model WelcomeViewModel
#using (Html.BeginForm("SignUp", "Member", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
....
<div class="form-group">
#Html.EditorFor(model => model.SignUp.CompanyName, new {htmlAttributes = new {#class = "form-control" }})
</div>
<div class="form-group">
#Html.EditorFor(model => model.SignUp.RegisteredNo, new {htmlAttributes = new {#class = "form-control" } })
</div>
....
<button type="submit" name="signup" class="btn">Register</button>
}
ViewModel:
public class WelcomeViewModel
{
public SignInViewModel LogOn { get; set; }
public SignUpViewModel SignUp { get; set; }
}
Action method:
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public virtual async Task<ActionResult> SignUp(SignUpViewModel model)
{
if (!ModelState.IsValid)
return View("SignIn", new WelcomeViewModel { SignUp = model });
// other code
return View();
}
When I post the data, the model gets null. I know the inputs will be generated like:
<input id="SignUp_CompanyName" name="SignUp.CompanyName">
But the model binder accepts this:
<input id="SignUp_CompanyName" name="CompanyName">
Now I want to know how can I remove that prefix? I know I can explicitly add name for each input:
#Html.TextBoxFor(model => model.SignUp.CompanyName, new { Name = "CompanyName" })
but I want to do it in a strongly type way.
Perhaps the easiest way would be to apply the [Bind] attribute with its Prefix set to "SignUp":
public async Task<ActionResult> SignUp([Bind(Prefix="SignUp")] SignUpViewModel model)
See MSDN

"Required" attribute not working when "AllowHtml" attribute is used. MVC 5

I want to post a html with a wysiwyg editor so I used [AllowHtml] attribute on my property. It works, but when I use it with [Required] and [StringLength] attributes, it stops working. ModelState.IsValid returns true even if prop is empty.
Yes, I know I can manually control it if it is null and add an error to ModelState, but why?
Why this happens? Is there a better way to post some HTML code to back-end?
My dto:
public int Id { get; set; }
[Required(ErrorMessage = CErr.RequiredField)]
[StringLength(100, ErrorMessage = CErr.Min5Max100)]
[MinLength(5, ErrorMessage = CErr.Min5Max100)]
public string Name { get; set; }
[AllowHtml]
[Required(ErrorMessage = CErr.RequiredField)]
[StringLength(4000, ErrorMessage = CErr.TooLongValue)]
public string HtmlBody { get; set; }
My action:
[Route("new")]
[HttpPost]
public ActionResult NewMessageLayout(ManageMessageLayoutDto model)
{
if (ModelState.IsValid)
{
var response = Repositories.MessageLayoutRepository.SaveMessageLayout(model, CurrentUser.Id);
if (response.Status == ResultStatus.Success)
{
return RedirectToAction("MessageManagement");
}
else
{
ModelState.AddModelError("error", response.Message);
return View("ManageMessageLayout", model);
}
}
return View("ManageMessageLayout", model);
}
And some HTML:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
<label>Name <span class="req">*</span></label>
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name, null, new { #class = "label label-danger" })
<label>Content <span class="req">*</span></label>
#Html.TextBoxFor(m => m.HtmlBody)
#Html.ValidationMessageFor(m => m.HtmlBody, null, new { #class = "label label-danger" })
<input class="btn btn-success btn-block btn-lg" type="submit" value="#(editing ? "Save" : "Add")" />
}
I am unable to replicate your error. However, I did notice a few things. First, according to the ckEditor documentation, textbox is not a valid attaching point for the editor. You should use textarea.
CkEditor Developer Documentation
At this point any textarea, p, or div element can be transformed into
a rich text editor by using the ckeditor() method.
Next, notice that I added AllowEmptyStrings=false to the [Required( attribute. This may be the important piece you are missing (will test without) - Testing seems to indicate that not setting AllowEmptyStrings does not impact the results of this test setup as the default value for this property is false. The model was still invalid.
Setup
VS 2015, MVC 5.2.2, .NET 4.5.1, jQuery 1.10.2, ckEditor 3.6.4
View Model
public class TestViewModel
{
[AllowHtml]
[Required(AllowEmptyStrings = false, ErrorMessage = "This should be filled out")]
[StringLength(4000, ErrorMessage="Its too big")]
public string HtmlBody { get; set; }
}
Controller Actions
public ActionResult About()
{
return View(new TestViewModel());
}
[HttpPost]
public ActionResult About(TestViewModel model)
{
if (!ModelState.IsValid) throw new Exception();
var test = model.HtmlBody;
return RedirectToAction("Contact");
}
View
#model WebApplication6.Models.TestViewModel
#{
ViewBag.Title = "About";
}
<h2>Test of HTML</h2>
#using (Html.BeginForm())
{
<label>Content <span class="req">*</span></label>
#Html.TextAreaFor(m => m.HtmlBody)
#Html.ValidationMessageFor(m => m.HtmlBody, null, new { #class = "label label-danger" })
<input type ="submit" value="test on server"/>
}
#section scripts
{
<script type="text/javascript">
$(function () {
$('#HtmlBody').ckeditor();
});
</script>
}
Results:
Basically, the exception was thrown because the model state was invalid
I found the problem.
My presentation layer was using 5.2.2.0 version of System.Web.Mvc but repository layer was using 5.2.3.0. I downgraded this to 5.2.2.0. And now it is working normally.

Client or server validation doesn't work with Data Annotations

I'm developing a MVC 4 application with Entity Framework 6 and encountered a problem with Data Annotations. No matter what annotation I use, it isn't validate.
I'm using the following view model to render a form:
public class UserViewModel
{
[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Name")]
[StringLength(100, MinimumLength = 2)]
public string FirstName { get; set; }
...
}
In the view:
#Html.LabelFor(m => m.FirstName)
#Html.EditorFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
And it gets rendered:
<label for="FirstName">First Name</label>
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
When I click the submit button the client validation doesn't get triggered and in the controller the ModelState.IsValid is true.
UPDATE:
Controller:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
return View(model);
}
[HttpPost]
public ActionResult Edit(UserViewModel model)
{
if (ModelState.IsValid == true)
{
_userService.Save(model);
return View(model);
}
return View(model);
}
The problem is that you POST to your controller, you map to User object, not to UserViewModel. This is one of the reasons your server-side validation is not triggered.
As for the client-side validation, I'm not sure, but could be just the same error - your view is based on User type, rather than on UserViewModel.
So your controller should look like this:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
var viewModel = new UserViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
// other properties
};
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(UserViewModel viewModel)
{
if (ModelState.IsValid == true)
{
var model = new User()
{
FirstName = viewModel.FirstName,
LastName = viewModel.LastName,
// other properties
};
_userService.Save(model);
return View(model);
}
return View(model);
}
And top of your Edit.cshtml you should have #model UserViewModel.
Hope this helps
After recreating my project I found out that the problem was the Ninject.Web.MVC dll. I didn't use this dll in my project so I removed it and it worked.

ASP.NET MVC Using DropDownLists

I am building a form using ASP.NET MVC which requires a drop-down list populated from a database.
I managed to get the drop-down list appearing, but no matter what I try to get the post data the application throws various errors.
In my Model:
public IEnumerable<SelectListItem> ApplicationTypeList { get; set; }
My Controller Get:
public ActionResult Create()
{
IEnumerable<SelectListItem> ApplicationTypeItems = Lists.ApplicationTypeList.Select(c => new SelectListItem { Value = c.Code, Text = c.Description });
ViewBag.AppTypes = ApplicationTypeItems;
return View();
}
Where c.Code is the value I want when the form is returned (string) and c.Description is just what is displayed.
The HttpPost Controller (auto-generated from scaffolding)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="PaymentID,ApplicationNumber,ApplicantName,ContactEmail,ContactAddress,ContactPhone")] Payment payment)
{
if (ModelState.IsValid)
{
db.Payments.Add(payment);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
For whatever reason it did not include ApplicationTypeList in the [Bind(Include=...
I also had to manually added this to the Create.cshtml View:
<div class="form-group">
#Html.LabelFor(model => model.ApplicationTypeList, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ApplicationTypeList", (IEnumerable<SelectListItem>)ViewBag.AppTypes, "Please Select");
#Html.ValidationMessageFor(model => model.ApplicationTypeList)
</div>
</div>
When I submit the form I get the following error on the view:
There is no ViewData item of type 'IEnumerable' that
has the key 'ApplicationTypeList'.
I have looked around and tried a couple of other ways of generating the list such as including a new String APplicationTypeCode and creating the list using #Html.DropDownListFor(model => model.ApplicationTypeCode, Model.ApplicationTypeList); but still get errors.
What is the best method of working with a form that includes drop-down lists and returning the selected value to the application?
Thanks.
I suggest create viewmodel
public class InsertPaymentViewModel {
public SelectList ApplicationTypeList {get; set;}
// property for selected item
[Display(Name = "Display name")]
[Required]
public int ApplicationTypeSelectedCode {get; set;}
}
In Get action use this model (I don like ViewBag for complex forms)
public ActionResult Create()
{
var model = new InsertPaymentViewModel();
model.ApplicationTypeItems = new SelectList(Lists.ApplicationTypeList, "Code", "Description ")
return View(model);
}
View:
#model PathTo.ViewModels.InsertPaymentViewModel
<div class="form-group">
#Html.LabelFor(model => model.ApplicationTypeSelectedCode , new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.ApplicationTypeSelectedCode, model.ApplicationTypeItems , "Please Select");
#Html.ValidationMessageFor(model => model.ApplicationTypeSelectedCode)
</div>
</div>
HttpPost action
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(InsertPaymentViewModel model)
{
if (ModelState.IsValid)
{
var payment = new Payment
{
code = model.InsertPaymentViewModel; //or find reference by code...
// ... another property etc.
}
db.Payments.Add(payment);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}

Resources