Check validations dynamically in Controller [ASP.Net MVC] - asp.net-mvc

I'm developing an MVC application for Travels portal. Here, I have a form to submit a new Travel request and my controller looks like this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(CreateOrEditCabBookingDto model)
{
if (ModelState.IsValid)
{
CabBookingAppService.Instance.CreateOrUpdate(model);
}
return PartialView("_CreateOrUpdateCabBooking", model);
}
I have not included any validation annotations to my Model because I want them to execute dynamically based on some conditions. Is it possible to have validations dynamically in controller and add them to Model State?
Example: Based on StatusId property value, set StartDate as Required.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(CreateOrEditCabBookingDto model)
{
if (ModelState.IsValid)
{
If(model.StatusId == 10)
{
// Check validation here
// Property "StartDate" is Required
}
CabBookingAppService.Instance.CreateOrUpdate(model);
}
return PartialView("_CreateOrUpdateCabBooking", model);
}
Based on the inputs, I changed like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(CreateOrEditCabBookingDto model)
{
if (string.IsNullOrEmpty(model.FromDateTime))
{
ModelState.AddModelError("FromDateTime","Date is Required");
}
if (ModelState.IsValid)
{
CabBookingAppService.Instance.CreateOrUpdate(model);
}
return PartialView("_CreateOrUpdateCabBooking", model);
}
And My View Looks like this:
#using (Ajax.BeginForm("CreateOrEdit", "Travel", null, new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "OnSuccess",
OnBegin = "OnBegin",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "PopupId"
}, new { id = "frm" }))
{
............................
<div class="form-group col-md-3">
#Html.LabelFor(model => model.FromDateTime)
#Html.TextBoxFor(model => model.FromDateTime, new { #class = "form-control datetimepicker", autocomplete = "off" })
#Html.ValidationMessageFor(model => model.FromDateTime)
</div>
............................
}

Related

submit button doesn't pass input from textbox

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

MVC model conflict using partial class and Validate

I have a page containing a form and a partial view (containing a form too).
both model have 1 (or more) properties with the same name. when I validate the first form, the value and validation message is duplicate on the second form.
I create a little sample with dummy entities.
person.cs
public partial class Person : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Person/Index.cshtml
#model Person
#{
ViewBag.Title = "Person";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Index", "Person", FormMethod.Post, new { id = "CreatePersonForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#Html.Partial("~/Views/Dog/Index.cshtml", new Dog())
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
PersonController.cs
public class PersonController : Controller
{
// GET: Person
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Person person)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(person);
}
}
I made a partial view practically the same.
Dog.cs
public partial class Dog : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Dog/Index.cshtml
#model Dog
#{
ViewBag.Title = "Dog Page";
}
#using (Html.BeginForm("Index", "Dog", FormMethod.Post, new { id = "CreateDogForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
DogController.cs
public class DogController : Controller
{
// GET: Dog
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Dog dog)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(dog);
}
}
if you start /Person/Index, if you write admin in the first textbox (person form), after posting (save) the second form (dog form) have the same text and validation than the first form.
The #Html.EditorFor by default uses the property name as the id and name of the generated HTML, and the validation uses these values to set the error messages! You can pass a value to overwrite that default behavior in your partial view as following:
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger", #data_valmsg_for="partial_name" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", #id="partial_name" } })

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

MVC 4 ajax form image upload fails

I am trying to add an image by using MVC 4 ajax form, but it always returns null value. I added my model, controller and view.
My View
#using (Ajax.BeginForm("Create", "Images", new AjaxOptions { HttpMethod = "Post", OnSuccess = "OnSuccess", OnFailure = "OnFailure", UpdateTargetId = "messageDiv" }, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-field">
#Html.LabelFor(model => model.Image.Description, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(model => model.Image.Description)
</div>
#Html.TextBoxFor(model => model.FileImage, new { type = "file" })
#Html.ValidationMessageFor(model => model.FileImage)
</div>...
}
My Model
public class ImageViewModel
{
public IList<Image> Images { get; set; }
public Image Image { get; set; }
public HttpPostedFileBase FileImage { get; set; }
}
My Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ImageViewModel model, HttpPostedFileBase FileImage)
{
if (ModelState.IsValid)
{
var x = FileImage.FileName;
var imageUrl = Path.GetFileName(model.FileImage.FileName);
...
}
}
In this example, I could not get the FileName. it returns always null value. Could you help me to solve this problem. I will be very happy.
I would prefer read the image using the request intead of trying to bind that to a model,
public ActionResult Create(ImageViewModel model)
{
if (Request.Files != null)
{
HttpPostedFileBase file = Request.Files[0]; //assuming that's going to be the first file
if (file.ContentLength > 0)
{
string fileName = Path.GetFileName(file.FileName);
string directory = Server.MapPath("/"); //change ths to your actual upload folder
file.SaveAs(Path.Combine(directory, fileName));
}
}
}

Html.HiddenFor with preset value

This part was sloved thanks to Ethan Brown
I want to set the value of my Html.HiddenFor helper with preset value
This is my code :
<%: Html.HiddenFor(model => model.idv, new { #value = ViewBag.id })%>
<%: Html.HiddenFor(model => model.etat, new { #value = "false" })%>
But when execute my code i get the error that model.idv and modele.etat are null.
This is seconde part no sloved till now :
This is my action :
public ActionResult Reserver(string id)
{
var model = new Models.rservation
{
idv = id,
etat = false
};
return View(model);
}
[HttpPost]
public ActionResult Reserver(Models.rservation model)
{
if (ModelState.IsValid)
{
entity.AddTorservation(model);
entity.SaveChanges();
return View();
}
else
{
return View(model);
}
}
And this is my view page :
<% using (Html.BeginForm("Reserver", "Home", FormMethod.Post, new { #class = "search_form" })) { %>
//some code textbox to fill
<input type="submit" value="Create" />
<% } %>
So when i click on submit button the model.idv is set again on null value
The correct way to set a preset value is to pass it in via the model (MVC appears to ignore the "value" parameter if you set it). To accomplish what you're looking for, in your action:
public ActionResult MyAction() {
var model = new MyModel {
idv = myPresetId,
etat = false
};
return View( model );
}
Then you don't have to do anything in your view except have:
<%: Html.HiddenFor( model => model.idv ) %>
<%: Html.HiddenFor( model => model.etat ) %>

Resources