View model using its value between actions in mvc - c#

I have a problem which I cant figure out how to fix. please note I'm very new to MVC.
Im designing a survey which has 8 questions. Im creating each question in a view.
What I need to is to keep the data between my views, but I lose the data even though I passed them through as a single view model.
Please help....
I have the following ViewModel
public class SurveyViewModelNew
{
public string description { get; set; }
public Question1ViewModel QuestionText1 { get; set; }
public Question2ViewModel QuestionText2 { get; set; }
public Question3ViewModel QuestionText3 { get; set; }
public Question4ViewModel QuestionText4 { get; set; }
public Question5ViewModel QuestionText5 { get; set; }
public Question6ViewModel QuestionText6 { get; set; }
public Question7ViewModel QuestionText7 { get; set; }
public Question8ViewModel QuestionText8 { get; set; }
}
And a view Model for each question:
public class Question1ViewModel
{
public string QuestionText { get; set; }
public AnswerViewModel Answers { get; set; }
}
and I here is how my view look like ?
#model survey.Models.SurveyViewModelNew
#using (Html.BeginForm("QuestionOneNext", "CreateSurveyStep2", FormMethod.Post, new { #class = "form-horizontal", role = "form", Model = Model }))
{
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Question 1: What question would you like to ask", htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-5">
#Html.EditorFor(model => model.QuestionText1.QuestionText, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.QuestionText, "", new { #class = "text-danger" })
</div>
</div>
<br/>
<br/>
<div class="form-group">
#Html.Label("Answer A", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextA, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextA, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer B", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextB, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextB, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer C", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextC, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextC, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer D", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextD, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextD, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer E", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextE, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextE, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer F", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextF, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextF, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer G", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextG, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextG, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer H", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.QuestionText1.Answers.AnswerTextH, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuestionText1.Answers.AnswerTextH, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer K", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Label("Not applicable", htmlAttributes: new { #class = "control-label col-md-2" })
</div>
</div>
<div class="form-group">
#Html.Label("Answer L", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.Label("Flag inappropriate question/refuse to answer", htmlAttributes: new { #class = "control-label col-md-5" })
</div>
</div>
<br/>
<br/>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Previous Step" class="btn btn-info" name="direction"/>
<input type="submit" value="Add a New Question to this survey" class="btn btn-danger" name="direction" />
<input type="submit" value="Countinue to submit this survey" class="btn btn-success" name="direction" />
</div>
</div>
</div>
}
and here is how my controller look like:
[HttpPost]
public ActionResult QuestionOneNext(SurveyViewModelNew surveyViewModel, string direction)
{
if (direction == "Countinue to submit this survey")
return RedirectToAction("Edit", "something", surveyViewModel);
if (direction == "Add a New Question to this survey")
{
return View("QuestionTwo", surveyViewModel);
//return RedirectToAction("QuestionTwo", "CreateSurveyStep2", surveyViewModel);
}
if (direction == "Previous Step")
return RedirectToAction("Index", "CreateSurveyStep1", surveyViewModel);
return View();
}
[HttpPost]
public ActionResult QuestionTwo(SurveyViewModelNew surveyViewModel, string direction)
{
if (direction == "Countinue to submit this survey")
{
return RedirectToAction("Edit", "NutStorage", surveyViewModel);
}
if (direction == "Add a New Question to this survey")
{
//return RedirectToAction("QuestionThree", "CreateSurveyStep2", surveyViewModel);
return View("QuestionThree", "CreateSurveyStep2", surveyViewModel);
}
if (direction == "Previous Step")
return RedirectToAction("QuestionOne", "CreateSurveyStep2", surveyViewModel);
return View();
}
what happens is when I get to "QuestionTwo" action in my controller I lose the answer to the first question.
I fixed this by using TemData, the following is my fixed controller:
[HttpGet]
public ActionResult QuestionOne(SurveyViewModelNew surveyViewModel)
{
var currentSurveyViewModel = (SurveyViewModelNew)TempData["SurveyView"];
if (currentSurveyViewModel != null)
{
if (currentSurveyViewModel.description != null)
{
surveyViewModel.description = currentSurveyViewModel.description;
}
if (currentSurveyViewModel.QuestionText1 != null)
{
surveyViewModel.QuestionText1 = currentSurveyViewModel.QuestionText1;
}
if (currentSurveyViewModel.QuestionText2 != null)
{
surveyViewModel.QuestionText2 = currentSurveyViewModel.QuestionText2;
}
if (currentSurveyViewModel.QuestionText3 != null)
{
surveyViewModel.QuestionText3 = currentSurveyViewModel.QuestionText3;
}
if (currentSurveyViewModel.QuestionText4 != null)
{
surveyViewModel.QuestionText4 = currentSurveyViewModel.QuestionText4;
}
if (currentSurveyViewModel.QuestionText5 != null)
{
surveyViewModel.QuestionText5 = currentSurveyViewModel.QuestionText5;
}
if (currentSurveyViewModel.QuestionText6 != null)
{
surveyViewModel.QuestionText6 = currentSurveyViewModel.QuestionText6;
}
if (currentSurveyViewModel.QuestionText7 != null)
{
surveyViewModel.QuestionText7 = currentSurveyViewModel.QuestionText7;
}
TempData["SurveyView"] = surveyViewModel;
}
if (surveyViewModel.description!= null)
{
surveyViewModel.description = surveyViewModel.description;
TempData["SurveyView"] = surveyViewModel;
}
return View("QuestionOne", surveyViewModel);
}
[HttpGet]
public ActionResult QuestionOneNext(SurveyViewModelNew surveyViewModel, string direction)
{
var currentSurveyViewModel = (SurveyViewModelNew)TempData["SurveyView"];
if (currentSurveyViewModel != null)
{
if (currentSurveyViewModel.description != null)
{
surveyViewModel.description = currentSurveyViewModel.description;
}
if (currentSurveyViewModel.QuestionText1 != null)
{
surveyViewModel.QuestionText1 = currentSurveyViewModel.QuestionText1;
}
if (currentSurveyViewModel.QuestionText2 != null)
{
surveyViewModel.QuestionText2 = currentSurveyViewModel.QuestionText2;
}
if (currentSurveyViewModel.QuestionText3 != null)
{
surveyViewModel.QuestionText3 = currentSurveyViewModel.QuestionText3;
}
if (currentSurveyViewModel.QuestionText4 != null)
{
surveyViewModel.QuestionText4 = currentSurveyViewModel.QuestionText4;
}
if (currentSurveyViewModel.QuestionText5 != null)
{
surveyViewModel.QuestionText5 = currentSurveyViewModel.QuestionText5;
}
if (currentSurveyViewModel.QuestionText6 != null)
{
surveyViewModel.QuestionText6 = currentSurveyViewModel.QuestionText6;
}
if (currentSurveyViewModel.QuestionText7 != null)
{
surveyViewModel.QuestionText7 = currentSurveyViewModel.QuestionText7;
}
}
if (direction == "Countinue to submit this survey")
return View("something", surveyViewModel);
else if (direction == "Add a New Question to this survey")
{
TempData["SurveyView"] = surveyViewModel;
return View("QuestionTwo", surveyViewModel);
}
else if (direction == "Previous Step")
{
TempData["SurveyView"] = surveyViewModel;
return View("Index", surveyViewModel);
}
else return View();
}

Instead of fighting the MVC framework in this way, here are a couple of other ways you could do it that might make more sense.
1)consider saving answer as you go to the database.
The record could be some kind of temporary record that isn't considered "real" until the end of the questionaire when the user does a final submit.
2) If the question data is not too big, render each question in it's own section (a div, a tab control) in the one view, and deal with switching between sections in javascript on button clicks. You could use your existing ViewModel to carry all questions into the view, and have them all posted and saved at the end. Make it a list of questions instead of 8 properties that are question, and you could even have a for loop in your view that renders a partial for each question. The partial could deal with next and previous buttons etc.

To pass the complete model from one page to the next, you have to ensure that all the properties of the model are part of the form and get posted back with each form submit operation back to the server.
To accomplish this, in your view you need to create hidden form fields, using #HTML.HiddenFor that contain all the data of the model even if you are not showing it to the user on a particular page.

Every time you make a synchronous HTTP request you are, in fact, reloading the same page, just with a different returned value. If you want the data to persist, you would benefit from learning more about asynchronous requests (AJAX) that can send/receive information without redirection...otherwise you need to persist the answer to question 1 and return it along with the view for question 2...then for question 3 you would need to ensure question 1 and 2 are persisted and return their values along with the context for question 3.
You 'could' go about this with hidden fields as mentioned, but you should really consider this is as a chance to learn something new and useful. Utilizing hidden fields is behind the times and really really obsolete.

Option 1: TempData
This same issue is addressed here: Can we pass model as a parameter in RedirectToAction?
The problem is your use of RedirectToAction. You cannot pass a model that way, only primitive types. You need to utilize TempData for this process:
[HttpPost]
public ActionResult QuestionOneNext(SurveyViewModelNew surveyViewModel, string direction)
{
var currentSurveyViewModel = surveyViewModel ?? (SurveyViewModel)TempData["SurveyView"];
if (direction == "Countinue to submit this survey")
{
TempData["SurveyView"] = currentSurveyViewModel;
return RedirectToAction("Edit", "something", null);
}
if (direction == "Add a New Question to this survey")
{
return View("QuestionTwo", currentSurveyViewModel);
//return RedirectToAction("QuestionTwo", "CreateSurveyStep2", surveyViewModel);
}
if (direction == "Previous Step")
{
TempData["SurveyView"] = currentSurveyViewModel;
return RedirectToAction("Index", "CreateSurveyStep1", null);
}
return View();
}
[HttpPost]
public ActionResult QuestionTwo(SurveyViewModelNew surveyViewModel, string direction)
{
var currentSurveyViewModel = surveyViewModel ?? (SurveyViewModel)TempData["SurveyView"];
if (direction == "Countinue to submit this survey")
{
TempData["SurveyView"] = currentSurveyViewModel ;
return RedirectToAction("Edit", "NutStorage", null);
}
if (direction == "Add a New Question to this survey")
{
//return RedirectToAction("QuestionThree", "CreateSurveyStep2", surveyViewModel);
return View("QuestionThree", "CreateSurveyStep2", currentSurveyViewModel );
}
if (direction == "Previous Step")
{
TempData["SurveyView"] = currentSurveyViewModel ;
return RedirectToAction("QuestionOne", "CreateSurveyStep2", null);
}
return View();
}
Option 2: RouteValueDictionary
There is another option for utilizing complex types and that is using the RouteValueDictionary. This is covered in this very similar question: Passing object in RedirectToAction
The general idea, for your RedirectToAction calls would be to do this:
return RedirectToAction("Edit", "NutStorage", new RouteValueDictionary(surveyViewModel));

Related

Issue With Login and Registration in asp.net mvc?

I m a student I m Learning MVC.
I have only one table for login and registration.
problem is login is not working as well as registration is not work
stanstuds.cs
public class stanstuds
{
[Key]
public int id { get; set; }
public string fname { get; set; }
public string mname { get; set; }
public string lname { get; set; }
public string country { get; set; }
public string usertype { get; set; }
public string username { get; set; }
public string password { get; set; }
}
context class
public class stanstudcontext:DbContext
{
public stanstudcontext():base("cnnos")
{
}
public DbSet<stanstuds> studs { get; set; }
}
controller
public class LogRegController : Controller
{
stanstudcontext context = new stanstudcontext();
//admin view just I print the sample message this is an admin view
public ActionResult Index()
{
return View();
}
//client view just I print the sample message this is a client view
public ActionResult Clientview()
{
return View();
}
//Login
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(stanstuds studs)
{
var userdata=context.studs.Where(x => x.username == studs.username && x.password == studs.password).FirstOrDefault();
if (userdata != null) //when i m debugging this condition is not check
{
// i have a 2 records of admin in database but when i run time enter that record login is not working
/// if (userdata.usertype == "admin")
//{
// return RedirectToAction("Index", "LogReg");
//}
//else
//{
// return RedirectToAction("Clientview", "LogReg");
//}
//}
/else
//{
// return View();
//}
if (userdata != null)
{
if (userdata.password == userdata.password) //success
{
//here give an error httpsessionstatebase does not contain a definition for setint32 and no extension method setnt32 are u missing directive or assembly reference???
HttpContext.Session.SetInt32("id", userdata.id);
if (userdata.usertype == "admin")
return RedirectToAction("Index", "LogReg");
else
return RedirectToAction("Clientview", "LogReg");
}
else
{
ViewBag.Message = "Invalid Password!";
return View();
}
}
else
{
ViewBag.Message = "Invalid Email!";
return View();
}
}
//Registration
public ActionResult Regis()
{
return View();
}
[HttpPost]
public ActionResult Regis(stanstuds studs)
{
context.studs.Add(studs);
context.SaveChanges();
return View();
}
}
Login.cshtml
#model LoginRegOneTable.Models.stanstuds
#{
ViewBag.Title = "Login";
}
<h2>Login</h2>
<html>
<head>
<title>Demo Website</title>
</head>
<body>
#using (Html.BeginForm())
{
<div>
<label>UserName:</label>
#Html.EditorFor(model => model.username)
</div>
<div>
<label>Password:</label>
#Html.EditorFor(model => model.password)
</div>
<div>
<input type="submit" value="Login" />
</div>
}
</body>
</html>
Regis.cshtml
#{
ViewBag.Title = "Regis";
}
<h2>Regis</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>stanstuds</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.fname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.fname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.fname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.mname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.mname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.mname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.lname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.lname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.lname, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.country, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.country, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.country, "", new { #class = "text-danger" })
</div>
</div>
**I think the problem is here I want to insert by default entry as a user but how???**
<div class="form-group">
#Html.LabelFor(model => model.usertype, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.usertype, new { htmlAttributes = new { #class = "form-control" ,#value = "User"} })
#Html.ValidationMessageFor(model => model.usertype, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.username, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.username, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.username, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.password, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
when user registration then insert by default entry as a user my registration is not working when I press register button then the record is not inserted and when I press login then login is not working? something is wrong my script?? please help??
In this case, you can check if the typed password is equal to the password in database, and check if it works:
var userdata = context.studs.Where(x => x.username.Equals(studs.username)).FirstOrDefault();
if (userdata != null)
{
if (userdata.Password == userdata.Password) //success
{
HttpContext.Session.SetInt32("id", userdata.id);
if (userdata.usertype == "admin")
return RedirectToAction("Index", "LogReg");
else
return RedirectToAction("Clientview", "LogReg");
}
else
{
ViewBag.Message = "Invalid Password!";
return View();
}
}
else
{
ViewBag.Message = "Invalid Email!";
return View();
}

Validation Message doesn't show for model as property in viewmodel

i've been struggling with Model validation message in asp.net
I've got a model used by a viewmodel.
I want my view to display validation errors if users don't fill required fields.
My ModelState.IsValid is false when required fields are not fill (expected behaviour) but i can't see
any error message
My Model Class :
public class Model
{
[Required(ErrorMessage = "Name is required.")]
public string Name { get; set; }
[Required(ErrorMessage = "Adress is required.")]
public string Adress { get; set; }
}
My ViewModel Class:
public class ViewModel
{
[Required]
public Model SelectedModel { get; set; }
public string Title { get; set;}
}
My Controller:
[HttpPost]
public ActionResult Create(ViewModel vm)
{
try
{
if (ModelState.IsValid)
{
bool result = *DatabaseStuff*
if(result == true)
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
return RedirectToAction("Index",vm);
}
catch
{
return View();
}
}
My View
#model ViewModel
#using (Html.BeginForm("Create", "MyController", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="box box-primary">
<div class="box-header with-border">
<h4 class="box-title">ViewModel Form</h4>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.SelectedModel.Name, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.SelectedModel.Name, null, "SelectedModel_Name",new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedModel.Name, "", new { #class = "text-danger", #data_valmsg_for = "SelectedModel_Name" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.SelectedModel.Adress, htmlAttributes: new { #class = "control-label" })
#Html.EditorFor(model => model.SelectedModel.Adress, null, "SelectedModel_Adress",new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SelectedModel.Adress, "", new { #class = "text-danger", #data_valmsg_for = "SelectedModel_Adress" })
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="box-footer">
<input type="submit" value="Create" class="btn btn-success pull-right" />
</div>
</div>
</div>
}
Thanks.
I believe that you should return View when the ModelState is invalid.
Return View doesn't make a new requests, it just renders the view
without changing URLs in the browser's address bar.
Return RedirectToAction makes a new request and the URL in the browser's
address bar is updated with the generated URL by MVC.
try
{
if (ModelState.IsValid)
{
bool result = *DatabaseStuff*
if(result == true)
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
return View();
}

Cannot make Byte a Required Field

Have an app where I would like to upload an image. I am using byte array for this.
The image is required but when I put that annotation on the variable and try create an image, it gives me the error message when it should have the image there. It also returns that the Model is Invalid.
When I remove the Required, it works but it can also be set as null which is what I don't want.
There doesn't seem to be a lot on the topic on Stack Overflow.
Here is my Model
[Key]
public int InvoiceId { get; set; }
[Required(ErrorMessage = "Company is required")]
public string Company { get; set; }
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
[Required(ErrorMessage = "Amount is required")]
public decimal Amount { get; set; }
[Required(ErrorMessage = "Picture of Invoice Required")]
public byte[] PictureOfInvoice { get; set; }
And my controller:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create([Bind(Include = "InvoiceId,Company,Description,Amount,PictureOfInvoice,DateReceived,ChurchId")] Invoice invoice,HttpPostedFileBase File)
{
if (ModelState.IsValid)
{
if (File != null && File.ContentLength > 0)
{
invoice.PictureOfInvoice = new byte[File.ContentLength];
File.InputStream.Read(invoice.PictureOfInvoice, 0, File.ContentLength);
}
else
{
TempData["Error"] = "Upload an Image";
}
db.Invoices.Add(invoice);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ChurchId = new SelectList(db.Churches, "ChurchId", "Name", invoice.ChurchId);
return View(invoice);
}
My View just in case its something there:
<h2>Add New Invoice</h2>
#if (TempData["Error"] != null)
{
<div style="color:red">#TempData["Error"]</div>
}
#using (Html.BeginForm("Create", "Invoices", FormMethod.Post, new { #class = "form-horizontal", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<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>
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Company, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Company, new { htmlAttributes = new { #class = "form-control", style = "width:20em;" } })
#Html.ValidationMessageFor(model => model.Company, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Amount, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Amount,new { htmlAttributes = new { #class = "form-control", style = "width:20em;" } })
#Html.ValidationMessageFor(model => model.Amount, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateReceived, "DateRecieved", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.DateReceived, new { htmlAttributes = new { #class = "form-control", style = "width:20em;" } })
#Html.ValidationMessageFor(model => model.DateReceived, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", style = "width:20em;" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PictureOfInvoice, "Picture of Invoice", htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
<input type="file" name="File" id="File" style="width: 50%;" />
#Html.ValidationMessageFor(model => model.PictureOfInvoice, "", new { #class = "text-danger" })
<output id="list"></output>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ChurchId, "Church Name", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ChurchId", null, htmlAttributes: new { #class = "form-control", style = "width:20em;" })
#Html.ValidationMessageFor(model => model.ChurchId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
<u>
#Html.ActionLink("Back to List", "Index")
</u>
</div>
<script src="~/Scripts/jquery.datetimepicker.js"></script>
<script>
$('#DateReceived').datetimepicker({
format: 'd/m/Y',
weeks: true,
disableWeekDays: [0, 1, 3, 4, 5, 6],
timepicker: false,
inline: false
});
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function (theFile) {
return function (e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'
].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('File').addEventListener('change', handleFileSelect, false);
</script>
The mixing of concerns here is cause problems. It appears you are trying to use an entity as the model for the view and have it satisfy both UI validation and persistence validation.
Separate the two concerns.
Create a view model specific to the desired behavior of the view.
The model should also include IEnumerable<SelectListItem> ChurchList property to populate the drop down.
public class CreateInvoiceViewModel {
[Required(ErrorMessage = "Company is required")]
public string Company { get; set; }
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
[Required(ErrorMessage = "Amount is required")]
public decimal Amount { get; set; }
[Required(ErrorMessage = "Picture of Invoice Required")]
public HttpPostedFileBase File { get; set; }
public int ChurchId { get; set; }
public IEnumerable<SelectListItem> ChurchList { get; set; }
//...other properties
}
and set that as the model of the view
#model CreateInvoiceViewModel
If creating a new invoice there is no id assigned as yet. That means when posting the model as you current have it, the model state cannot be valid as InvoiceId, which is tagged as Required is not provided.
The uploaded file (invoice picture) should also be included in the view model and should use #Html.TextBoxFor(m => m.File, new { type = "file" }) to get client side validation. The model binder will set that property based on whether a matching input was provided.
<div class="form-group">
#Html.LabelFor(model => model.File, "Picture of Invoice", htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
#Html.TextBoxFor(model => model.File, new { type = "file", style = "width: 50%;"})
#Html.ValidationMessageFor(model => model.File, "", new { #class = "text-danger" })
<output id="list"></output>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ChurchId, "Church Name", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.ChurchId, (IEnumerable<SelectListItem>)#Model.ChurchList, htmlAttributes: new { #class = "form-control", style = "width:20em;" })
#Html.ValidationMessageFor(model => model.ChurchId, "", new { #class = "text-danger" })
</div>
</div>
So now on the controller side, [Bind] should be removed as it's not needed when using a view model.
[HttpGet]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create() {
var model = new CreateInvoiceViewModel {
//set default property values as needed
};
model.ChurchList = new SelectList(db.Churches, "ChurchId", "Name");
//...
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create(CreateInvoiceViewModel model) {
if (ModelState.IsValid) {
var file = model.File;
//copy properties over to entity
var invoice = new Invoice {
Company = model.Company,
Description = model.Description,
Amount = model.Amount,
DateReceived = model.DateReceived,
ChurchId = model.ChurchId,
//create array for file contents
PictureOfInvoice = new byte[file.ContentLength]
};
//populate byte array
file.InputStream.Read(invoice.PictureOfInvoice, 0, file.ContentLength);
db.Invoices.Add(invoice);
db.SaveChanges();
return RedirectToAction("Index");
}
//if we get this far model state is invalid.
//return view with validation errors.
model.ChurchList = new SelectList(db.Churches, "ChurchId", "Name", model.ChurchId);
return View(model);
}
The model state will provide the necessary feedback if the model requirements are not valid. Removing the need for using the temp data and the file check.
You're not using data binding to populate PictureOfInvoice in your model - you're doing it inside your controller's method. But you're only performing that step after confirming that the model is valid, which it cannot be by this logic.
I think you can try just switching the first bits around:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Parish Admin, Priest, Administrator")]
public ActionResult Create(
[Bind(Include = "InvoiceId,Company,Description,Amount,DateReceived,ChurchId")]
Invoice invoice,
HttpPostedFileBase File)
{
if (invoice!=null && File != null && File.ContentLength > 0)
{
invoice.PictureOfInvoice = new byte[File.ContentLength];
File.InputStream.Read(invoice.PictureOfInvoice, 0, File.ContentLength);
}
if (ModelState.IsValid)
{
//Proceed
If that's still not letting the model pass validation then you may have to just continue applying errors as you previously were in the else

ASP.NET Collection empty after POST

Despite incorporating all advice I found in other questions and this article
the List vsValues passed to the view is always empty after POST.
View
#model OTS.ParcelOrder
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ParcelOrder</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.otsID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.otsID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.otsID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.parcelID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.parcelID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.parcelID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.recipientCountry, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.recipientCountry, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.recipientCountry, "", new { #class = "text-danger" })
</div>
</div>
#for (int i = 0; i < Model.vsValues.Count; i++)
{
#Html.Label(Model.ParcelOrder_VSFields.ElementAt(i).VendorSpecifiedInfoField.fieldName,
htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.vsValues[i], new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.vsValues[i], "", new { #class = "text-danger" })
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
// GET: ParcelOrders/Create
public ActionResult Create(int vendorId = 1)
{
ParcelOrder order = new ParcelOrder(vendorId);
return View(order);
}
// POST: ParcelOrders/Create
// Aktivieren Sie zum Schutz vor übermäßigem Senden von Angriffen die spezifischen Eigenschaften, mit denen eine Bindung erfolgen soll. Weitere Informationen
// finden Sie unter http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ParcelOrder parcelOrder)
{
parcelOrder.customerID = User.Identity.GetUserId();
if (ModelState.IsValid)
{
db.ParcelOrder.Add(parcelOrder);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(parcelOrder);
}
public partial class ParcelOrder
{
private Entities db = new Entities();
public List<string> vsValues = new List<string>();
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ParcelOrder()
{
this.ParcelOrder_VSFields = new List<ParcelOrder_VSFields>();
}
public ParcelOrder(int vendorId)
{
this.ParcelOrder_VSFields = new List<ParcelOrder_VSFields>();
var vendorQuery = from vsif in db.VendorSpecifiedInfoField
where vsif.vendorID == vendorId
select vsif;
foreach (var vsif in vendorQuery)
{
vsValues.Add("");
this.ParcelOrder_VSFields.Add(new OTS.ParcelOrder_VSFields
{
vsFieldID = vsif.id,
VendorSpecifiedInfoField = vsif,
value = ""
});
}
}
public string otsID { get; set; }
public string parcelID { get; set; }
public string customerID { get; set; }
public string recipientCountry { get; set; }
public virtual AspNetUsers AspNetUsers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ParcelOrder_VSFields> ParcelOrder_VSFields { get; set; }
}
}
Note
The values are supposed to be POSTed in the List vsValues and will later be set as properties of ParcelOrder_VSFields inside the controller to avoid POSTing redundant information.
It's because you are posting nothing to no where look at that
#using (Html.BeginForm())
Should rather be like:
#using (Html.BeginForm("Create","ParcelOrders",FormMethod.Post))
Update
Also after that. Your Model looks wrong to me, if you want to pass values to a list i suggest you have a list property of same kind [this property needs to be with in your model ParcelOrder and not virtual]. then within the parameter-less constructor of the class do your foreach. track it within every step you see your issue.

A promise chain failed to handle a rejection in MVC5

I have the following validation method and the associated model class. I am getting the above mentioned error (Title of the Question) in my browser while clicking on the submit button for Create.
How can I resolve the particular issue?
Model Class
public class LsystemFamily
{
public int LsystemFamilyID { get; set; }
[Display (Name = "Family Name")]
[Remote("DuplicateFamilyName","LsystemFamilies",HttpMethod = "POST",ErrorMessage= "System Family Name already Exists",AdditionalFields= "LsystemFamilyID")]
//[Unique(ErrorMessage = "Family Name Already Exists")]
public string FamilyName { get; set; }
Validation Method
public JsonResult DuplicateFamilyName(string FamilyName, int FamilyID)
{
//bool idExists = db.LsystemFamily.Any(id=>id.LsystemFamilyID.Equals(FamilyID));
if (FamilyID == 0)
{
bool exists = db.LsystemFamily.Any(x => x.FamilyName == FamilyName);
//var name = db.LsystemFamily.Where(x => x.FamilyName.Equals(FamilyName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
return Json(!exists,JsonRequestBehavior.AllowGet);
}
else
{
bool exists = db.LsystemFamily.Where(x => x.LsystemFamilyID != FamilyID).Any(x => x.FamilyName == FamilyName);
//var name = db.LsystemFamily.Where(x => x.FamilyName.Equals(FamilyName, StringComparison.CurrentCultureIgnoreCase) && x.LsystemFamilyID != FamilyID).FirstOrDefault();
return Json(!exists, JsonRequestBehavior.AllowGet);
}
}
The validation method is posted because I started getting the error only after adding the Json Validation.
EDIT : View
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.LsystemFamilyID);
#Html.LabelFor(model => model.FamilyName, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.FamilyName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FamilyName, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.DescriptionEN, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.DescriptionEN, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DescriptionEN, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.DescriptionDE, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.DescriptionDE, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DescriptionDE, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
#Html.ActionLink("Back to List", "Index")
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}

Categories