MVC model validation - asp.net-mvc

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);
}

Related

How to pass different list of data from view to controller MVC

currently I facing a very tricky problem of passing different list of data from view to controller.
I have created two input box to submit my data to controller so that it can be saved into CreateAccountsDB and further display it in the list of
Selected Subcon when Create button is pressed.
The problem I face here is:
when pressing the Create button with entered data from NewCompanyName textbox and NewEmail textbox, those entered data do pass data from View to Controller and save data into CreateAccountDB (not showing in View), but the entered data is not displaying in the list of Selected Subcon.
Create View
Here is the model.
public class Tender
{
public int ID { get; set; }
public string CompanyName { get; set; }
public List<CreateAccount> FrequentCompanyName { get; set; }
public List<CreateAccount> SuggestCompanyName { get; set; }
public List<CreateAccount> SelectedCompanyName { get; set; }
public string CompanyNameNew { get; set; }
public string EmailNew { get; set; }
public int? TradeID { get; set; }
public virtual Trade Trade { get; set; }
public int? CreateAccountID { get; set; }
public virtual CreateAccount CreateAccount { get; set; }
}
Here is the Get Method of Create function in controller:
[httpGet]
public ActionResult Create(int? id)
{
Tender tender = new Tender();
tender.FrequentCompanyName = db.createaccountDB.Include(tm => tm.Trade).Where(td => td.Frequency == 32).ToList();
tender.SuggestCompanyName = db.createaccountDB.Include(tm => tm.Trade).ToList();
if (tender.SelectedCompanyName == null)
{
tender.SelectedCompanyName = new List<CreateAccount>().ToList();
}
return View(tender);
}
and Here is my Post Method of Create function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,CompanyName,TradeID,FrequentCompanyName,SelectedCompanyName,CreateAccountID")] Tender tender ,string CompanyNameNew, string Emailnew)
{
CreateAccount accnew = new CreateAccount();
accnew.CompanyName = CompanyNameNew;
accnew.Email = Emailnew;
if(ModelState.IsValid)
{
db.createaccountDB.Add(accnew);
db.SaveChanges();
}
if (tender.SelectedCompanyName == null)
{
tender.SelectedCompanyName = new List<CreateAccount>().ToList();
}
tender.FrequentCompanyName = db.createaccountDB.Include(tm => tm.Trade).Where(td => td.Frequency == 32).ToList();
tender.SuggestCompanyName = db.createaccountDB.Include(tm => tm.Trade).ToList();
tender.SelectedCompanyName.ToList().Add(accnew);
return View(tender);
}
and Here is my Create View:
#model Tandelion0.Models.Tender
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-group">
#*#Html.LabelFor(model => model.ProjectName, htmlAttributes: new { #class = "control-label col-md-3" })*#
<div class="col-md-3">
<h5>New Company Name</h5>
#Html.EditorFor(model => model.CompanyNameNew, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CompanyNameNew, "", new { #class = "text-danger" })
</div>
<div class="col-md-3">
<h5>New Email</h5>
#Html.EditorFor(model => model.EmailNew, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EmailNew, "", new { #class = "text-danger" })
</div>
<div class="container" align="center">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
<div class="container row">
<!--selected subcon column-->
<div class="container row col-sm-4">
<h4>
Selected Subcon
</h4>
<div style="overflow-y: scroll; height:250px;">
<table class="table table-hover">
#foreach (var item in Model.SelectedCompanyName)
{
<tbody>
<tr>
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
</tr>
</tbody>
}
</table>
</div>
</div>
</div>
}
So far I manage to save data from view into CreateAccountsDB when create button is pressed, but those data just couldn't pass it from Post method Create function to Get method Create function in Controller. The data and the list become null immediate after come out from post method Create function.
Because of data becomes null, the view couldn't receive any data from controller.
May I know how can i solve the the problem of passing data from controller to view? Is the way I pass data totally wrong?
Any advice is truly appreciated.
In your HttpPost Action method :
Instead of :
tender.SelectedCompanyName.ToList().Add(accnew);
You should be doing:
tender.SelectedCompanyName.Add(accnew);
Calling ToList().Add(object) won't actually add to SelectedCompanyName.Instead it will add to the new list object created by calling ToList() method which you are not assigning back to tender.SelectedCompanyName.
A better approach however would be to use Post/Redirect/Get Pattern.
Instead of returning a view from your post method , do a temorary redirect to your [HttpGet]Create action method passing the id of the tender.

MVC-5 Razor Validation Errors showing on page load

I have an insert page when I go to the insert page all the validation field is being shown,
[Required(ErrorMessage ="Please Enter Name")]
public string ccname { get; set; }
This is my class where I declared string ccname with required validation message
ENTER NAME
and it supposed to appear when user clicks on insert without entering data in ccname
but validatation message is being shown on the page load
#Html.TextBoxFor(model => model.ccname, new { #class = "textboxstyle" })
#Html.ValidationMessageFor(model => model.ccname)
I tried few things but nothing works,
here is an example
in my controller I added ModelState.clear();
public ActionResult insert()
{
ModelState.Clear();
return View();
}
and in my view I changed the code from
#Html.TextBoxFor(model => model.ccname, new { #class = "textboxstyle" })
#Html.ValidationMessageFor(model => model.ccname)
to
#Html.TextBoxFor(model => model.ccname, new { #class = "textboxstyle" })
#Html.ValidationMessageFor(model => model.ccname,"",new {#style= ".validation-summary-valid { display:none; }" })
but neither of these works
what should I do now?
Example:
Model:
public class MyModel
{
[Required(ErrorMessage ="Please Enter Name")]
public string ccname { get; set; }
}
Controller:
public class HomeController:Controller
{
[HttpGet]
ActionResult Insert()
{
var model =new MyModel();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
ActionResult Insert(MyModel model)
{
if(ModelState.IsValid)
{
//Do something
return View();
}
return View(model);
}
}
View
Insert.cshtml
#model MyModel
#using (Html.BeginForm("Insert", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.TextBoxFor(model => model.ccname, new { #class = "textboxstyle" })
#Html.ValidationMessageFor(model => model.ccname)
<input type="submit" value="Insert" class="btn btn-primary" />
}
#Scripts.Render("~/bundles/jqueryval")

Losing form values after validation

Using a ViewModel for validation:
public class CCvm
{
[Required(ErrorMessage = "Please enter your Name")]
public string cardHolderName { get; set; }
}
My controller calls a task on post:
public async Task<ActionResult> Pay(FormCollection form, CCvm model)
{
if (!ModelState.IsValid)
{
return View(model);
}
}
And the View:
#model GCwholesale.Models.CCvm
#{
Layout = "~/Views/Shared/_HomeSubPageLayout.cshtml";
ViewBag.Title = "Secure Checkout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="Payment">
<label>Name on Card: </label>
#Html.EditorFor(model => model.cardHolderName, new { htmlAttributes = new { #placeholder = "Cardholder Name Please", #Value = ViewBag.Name } })<br />
#Html.ValidationMessageFor(model => model.cardHolderName)
<button class="submitCheckout">SUBMIT NOW</button>
</div>
}
But when validation fails the data in the form goes away.
Thanks for taking a look.
You do not need to set #Value = ViewBag.Name inside EditorFor.
#Html.EditorFor(model => model.cardHolderName,
new { htmlAttributes = new { #placeholder = "Cardholder Name Please" } })
Besides, you do not need FormCollection as a parameter because you already have CCvm Model.
public async Task<ActionResult> Pay(CCvm model){
{
//...
}
#Value = ViewBag.Name
You're not setting the ViewBag.Name, so it wouldn't have a value and would result in a blank input. Remove that and let the HtmlHelper set it based off the value in the posted model.

UpdateModel not updating my model

I must have something incorrectly setup as I can't get the UpdateModel function to correctly update my model based on information passed in via a FormCollection.
My View looks like:
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
<p>ID: #Html.TextBox("ID", Model.ID)</p>
<p>Forename: #Html.TextBox("Forename", Model.Forename)</p>
<p>Surname: #Html.TextBox("Surname", Model.Surname)</p>
<input type="submit" value="Submit" />
}
</fieldset>
My model is:
namespace NSLM.Models
{
public class Person
{
public int ID;
public string Forename;
public string Surname;
}
}
and my controller is:
[HttpPost]
public ActionResult Details(FormCollection collection)
{
try
{
// TODO: Add update logic here
Models.Person m = new Models.Person();
// This doesn't work i.e. the model is not updated with the form values
TryUpdateModel(m);
// This does work
int.TryParse(Request.Form["ID"], out m.ID);
m.Forename = Request.Form["Forename"];
m.Surname = Request.Form["Surname"];
return View(m);
}
catch
{
return View();
}
}
as you can see if I manually assign each property it works fine, so what have I not set that would get the model to be updated with the form values?
Thanks,
Mark
Replace fields with properties in your model, i.e.:
namespace NSLM.Models
{
public class Person
{
public int ID {get; set;}
public string Forename {get; set;}
public string Surname {get; set;}
}
}
By the time the call gets to the action method any automatic model binding has already been performed. Try changing the input parameter of your action method to accept a Person instance. In that case the model binder will try to create the instance and populate it from the values passed by your form.
try this :
view :
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
<p>Forename: #Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</p>
<p>Surname: #Html.EditorFor(model => model.Surname)
#Html.ValidationMessageFor(model => model.Surname)
</p>
<input type="submit" value="Submit" />
}
</fieldset>
Controller :
[HttpPost]
public ActionResult Details(Person p)
{
if (ModelState.IsValid)
{
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(p);
}

how to select dropdownlist value and display in mvc3?

I have an MVC3 web application. On index.cshtml I have two dropdown lists. When I select from those lists I need to click on a next button and I want to display the selected values. How can i do this?
homecontroller.cs
DataRepository objRepository = new DataRepository();
public ActionResult Index()
{
ViewModel objViewModel = new ViewModel();
objViewModel.ID = objRepository.GetPricingSecurityID();
objViewModel.ddlId = objRepository.GetCUSIP();
return View(objViewModel);
}
ViewModel.cs
public class ViewModel
{
//DDL ID
[Required(ErrorMessage = "Please select a PricingSecurityID")]
public List<SelectListItem> ddlId { get; set; }
//DropDownList Values
[Required(ErrorMessage = "Please select a PricingSecurityID")]
public List<SelectListItem> ID { get; set; }
}
index.cshtml
<div class="editor-label">
#Html.Label("Pricing SecurityID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ID,
new SelectList(Model.ID, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ID)
</div>
<div class="editor-label">
#Html.Label("CUSIP ID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ddlId,
new SelectList(Model.ddlId, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ddlId)
</div>
<p>
<input type="submit" value="Next" />
</p>
How can I display selected values?
If your requirement is to build some kind of wizard, you need a way of maintaining state between steps.
ViewBag is no good for this because you should be following the PRG (Post/Redirect/Get) pattern for each wizard step.
TempData would work for navigating forward between steps but will fall over if the user goes back or navigates to a step directly.
You therefore need something with a longer lifetime. The ASP.NET Session object or a database are both good candidates for this.
Here's an example:
public class WizardController : Controller
{
public ActionResult Step1()
{
var session = GetWizardSession();
if (session.Step1 == null)
{
session.Step1 = new Step1View
{
PricingSecurityIds = new SelectList(new[] { 1, 2, 3, 4, 5 }),
SomeOtherIds = new SelectList(new[] { 1, 2, 3, 4, 5 })
};
}
return View(session.Step1);
}
[HttpPost]
public ActionResult Step1(Step1View cmd)
{
var session = GetWizardSession();
// save the wizard state
session.Step1.SelectedPricingSecurityId = cmd.SelectedPricingSecurityId;
session.Step1.SelectedSomeOtherId = cmd.SelectedSomeOtherId;
// now onto step 2
session.Step2 = new Step2View
{
PricingSecurityId = cmd.SelectedPricingSecurityId,
SomeOtherId = cmd.SelectedSomeOtherId,
Name = "John Smith"
};
return RedirectToAction("step2");
}
public ActionResult Step2()
{
return View(GetWizardSession().Step2);
}
public WizardSession GetWizardSession()
{
var session = Session["wizardsession"];
if (session == null)
{
session = new WizardSession();
Session["wizardsession"] = session;
}
return session as WizardSession;
}
}
public class Step1View
{
public SelectList PricingSecurityIds { get; set; }
public SelectList SomeOtherIds { get; set; }
public int SelectedPricingSecurityId { get; set; }
public int SelectedSomeOtherId { get; set; }
}
public class Step2View
{
public int PricingSecurityId { get; set; }
public int SomeOtherId { get; set; }
public string Name { get; set; }
}
public class WizardSession
{
public Step1View Step1 { get; set; }
public Step2View Step2 { get; set; }
}
In Step1 we make a call to GetWizardSession. This returns an object from the ASP.NET Session that contains all of the information we have collected for each step in the wizard. In this example we simply store the ViewModel for each step (i.e. session.Step1).
We check to see if Step1 exists in the session and create it if it doesn't. We then pass the Step1 model to our view.
When the user submits the form we update the "Selected" values in session.Step1. This ensures that if the user navigates back to /step1, we "remember" their values. We then build the model for Step2 and save it in the session.
When we navigate to /step2 we assume that a model exists in the session (because they should have got here from step1) so we just return return View(GetWizardSession().Step2);
The views:
Step 1
#model MvcWizardDemo.Controllers.Step1View
#{
ViewBag.Title = "Step1";
}
<h2>Step1</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Step1View</legend>
<div class="editor-label">
#Html.LabelFor(m => m.PricingSecurityIds)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedPricingSecurityId, Model.PricingSecurityIds)
#Html.ValidationMessageFor(m => m.PricingSecurityIds)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.SomeOtherIds)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedSomeOtherId, Model.SomeOtherIds)
#Html.ValidationMessageFor(m => m.SomeOtherIds)
</div>
<p>
<input type="submit" value="Next" />
</p>
</fieldset>
}
Step 2
#model MvcWizardDemo.Controllers.Step2View
#{
ViewBag.Title = "Step2";
}
<h2>Step2</h2>
Hi, #Model.Name you selected the following values in the previous step:
<p>
<strong>Security Id:</strong> #Model.PricingSecurityId
</p>
<p>
<strong>Some other Id:</strong> #Model.SomeOtherId
</p>
Try this it should work :
[HttpPost]
public ActionResult Index(ViewModel model)
{
// put what you want to show
}

Resources