How to display a submit success message after posting a form - c#

I want to show a successfully submit message on the Index page when user successfully submit on the Register page.
My Register action includes:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveRegister(AirlineWebApplication.Models.User User, HttpPostedFileBase file)
{
db.Users.Add(User);
db.SaveChanges();
return RedirectToAction("Index");
}
My Register view as follows includes.
<section class="registersection">
<div class="container-fluid">
<div class="row">
<div class="signupForm">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
#using (Html.BeginForm("SaveRegister", "Users", FormMethod.Post)) {
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<div class="editor-field">
<label>Name
#Html.EditorFor(model => model.user_name)
</label>
#Html.ValidationMessageFor(model => model.user_name)
</div>
<div class="editor-field">
<label>Password
#Html.EditorFor(model => model.password)
</label>
#Html.ValidationMessageFor(model => model.password)
</div>
<p>
<input type="submit" value="Register" />
</p>
}
</div>
</div>
</div>
</div>
</section>
How can I display the successful message "You have registered successfully" on Index page when I press the Register button?

There are many ways to do that. For example in your controller use TempData to know where you are coming from:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveRegister(AirlineWebApplication.Models.User User,
HttpPostedFileBase file)
{
db.Users.Add(User);
db.SaveChanges();
TempData["Referrer"] = "SaveRegister";
return RedirectToAction("Index");
}
In your Index view:
#if((string)TempData["Referrer"] == "SaveRegister")
{
<div class="alert alert-success">
<strong>You have registered successfully</strong>
</div>
}
Unlike ViewBag, TempData would persist to the next request so that it's suitable for redirecting to a different page and retrieving the value from there.

When you create a new project in Visual Studio, you can see examples of how to display confirmation messages.
You can create a new view, say RegisterConfirmation and then in your controller, change the last line from:
return RedirectToAction("Index");
to:
return View("RegisterConfirmation");
EDIT Displaying the message on the main Index page is not recommended. If you want to do so, you need to pass the message to the Index page:
string message = "You have registered successfully";
return RedirectToAction("Index", "Home", new { m = message });
Then in the controller of the index page, you need to grab the message from the query string and add it to the model or the ViewBag:
public ActionResult Index(string m) {
ViewBag.Message = m;
}
And finally on your index view, you can display the message in a div wherever you want on the page:
<div>#ViewBag.Message</div>
Alternatively, you can make the div a popup. This is pretty simple with CSS.
If you don't want to use the query string (which is probably better), you can use TempData instead:
TempData["Message"] = "You have registered successfully";
return RedirectToAction("Index", "Home");
And in the Index view:
<div>#TempData["Message"]</div>

Related

Blank form appears after submitting in ASP.NET Core?

I have an application that has multiple tabs to submit a form. However, users are getting a blank form after hitting submit, and are not redirected to the confirmation page. This is odd because some applications are submitting fine, while others are not storing the data from fields to the database. My first guess was that it was a firewall issue. I am using a post method for submit. Another note, this is something that is occurring on our production server but not on our local development environment.
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Applicant")]
public IActionResult Application(ApplicationViewModel model)
{
var saved = false;
model = _applicantManager.RebuildApplicationViewModel(model);
if (ModelState.IsValid)
{
saved = _applicantManager.SubmitApplication(model, CurrentUser.UserName);
return RedirectToAction("Index");
}
return View(model);
}
Form (Rather large so shortened for simplicity):
#Html.ValidationSummary(excludePropertyErrors: false, message: "", htmlAttributes: new { #style = "color:red" })
<ul class="wdn_tabs">
<li>Personal</li>
<li>Academic</li>
<li>Questions</li>
<li>Availability & Reference</li>
</ul>
#using (Html.BeginForm(actionName: "Application", controllerName: "Applicant", method: FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="wdn_tabs_content">
<div id="personalTab">
<h4>Personal Information</h4>
<div style="color:red"><strong>All fields are required.</strong></div>
<div class="tabBody">
<div class="inputGroup spacer">
<strong>#Html.DisplayNameFor(x => x.PersonalInfo.FirstName)</strong>
#Html.ValidationMessageFor(x => x.PersonalInfo.FirstName)
#Html.TextBoxFor(x => x.PersonalInfo.FirstName)
</div>
<div class="inputGroup spacer">
<strong>#Html.DisplayNameFor(x => x.PersonalInfo.LastName)</strong>
#Html.ValidationMessageFor(x => x.PersonalInfo.LastName)
<input type="text" asp-for="PersonalInfo.LastName" />
</div>
..........................other fields..........................
</div>
<div class="tabFooter">
Back
<button type="submit" class="wdn-button wdn-button-complement">Submit</button>
</div>
</div>
</div>
}

MVC submit isn't returning all data

I'm writing an MVC app which ends up accessing a SQL database. On my edit page, I previously had every item available to be edited that is in the model. Recently I was asked to no longer allow the user to edit the primary keys. I did a quick change to change the primary key fields (in this example, there are 2 of them) from an EditorFor to a DisplayFor. The new view is this:
#model App.Data.Item
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Item</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<strong>ID:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID)</p>
</div>
</div>
<div class="form-group">
<strong>ID2:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID2)</p>
</div>
</div>
<div class="form-group">
<strong>Description:</strong>
<div class="col-md-10">
<p>#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)</p>
</div>
</div>
<br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-primary">Submit <i class="fa fa-caret-right"></i></button>
</div>
</div>
</div>
}
It used to work with the full editing. Now the data is displayed properly, as expected. However, when submit is pressed, Null values are passed back to the controller for the values that are displayed.
These are the edit functions in my controller. ItemService.Edit() just saves the data to the server. It works correctly.
[Authorize]
public ActionResult Edit(string id)
{
if (id == null)
{
//return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string[] vals = id.Split('|');
ItemAttribute itemAttribute = itemAttributeService.Find(int.Parse(vals[0]), vals[1]);
if (itemAttribute == null)
{
return HttpNotFound();
}
return View(itemAttribute);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Edit([Bind(Include = "ID,ID2,Description")]
Item item)
{
if (item.Description == null)
{
ModelState.AddModelError("Description", "Description cannot be null.");
}
if (ModelState.IsValid)
{
itemService.Edit(item);
return RedirectToAction("../Home/Index/");
}
return View(item);
}
Lastly, my data model:
public class Item
{
public int ID { get; set; }
public string ID2 { get; set; }
public string Description { get; set; }
}
Why is the data no longer being passed back to the second function, and how do I get it to pass correctly so that I can save it to the database?
You need to have an input element generated for the items that you want returned. Currently, you are only displaying two of your model elements and have no associated input with them. As a result, they will not be POSTed back to the server.
To get them to post to the server and not "editable" from a textbox, add a Html.HiddenFor() helper for each of the items that you need returned.
<div class="form-group">
<strong>ID:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID)</p>
<p>#Html.HiddenFor(model => model.ID)</p>
</div>
</div>
<div class="form-group">
<strong>ID2:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID2)</p>
<p>#Html.HiddenFor(model => model.ID)</p>
</div>
</div>
However, keep in mind that anyone can edit the HTML using Firebug or Chrome console tools and submit any value that they want for any field, even if you did not want to change it. Be sure that when you are persisting the changes to the database, you are NOT including these fields as part of the update.
Try this, just before this line of code:
#Html.DisplayFor(model => model.ID)
put in this for debugging:
#Html.LabelFor(model => model.ID)
Tell us what you see...
If you see the label then check your controller, in particular the parameter it takes on the post. It should take and Item of type ITEM per your model.
Before the controller receives the data MVC has to try to populate the data... It converts name/value pairs to model types with values secretly. If you don't see any data after you are in the controller it's usually because the names were not found!

When using Html.Action in layout getting error: Child actions are not allowed to perform redirect actions

I'll try to keep this as brief as possible.. I have a ViewResult that I am rendering on my _layout.cshtml page. This ViewResult is in a controller called CommonController where I am keeping some actions and data that I need on every page. If it matters, my ViewResult is a simple form.
ViewResult and the CommonController
[Authorize]
public class CommonController : AuthenticatedBaseController
{
[ChildActionOnly]
public ViewResult OrgSwitch()
{
//do stuff
return View();
}
[HttpPost]
public RedirectToRouteResult OrgSwitch(string UserOrgs)
{
return RedirectToAction("Index", "Recruiter", new { orgId = UserOrgs, area = "InkScroll" });
}
}
In my _Layout.cshtml page I render it like so:
#Html.Action("OrgSwitch", new { controller = "Common", area = "MyArea" })
This is all working fine, but now I am adding some functionality elsewhere in the app. I had asp.net mvc5 scaffold out a controller and views for a class I need to CRUD against. Just the quick and dirty forms so I can test a couple of things. I am going into the create page to add a new item. warning, it is mvc scaffolding, very ugly:
#model inkScroll.Models.Vacancy
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Vacancy</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.JobRef, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobRef)
#Html.ValidationMessageFor(model => model.JobRef)
</div>
</div>
// lots more ugly code
<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>
}
When I click 'submit' in the controller it does the usually validation checks and stuff. If my form is valid, there is no problem. If there are modelstate issues, I get the child action error.
Any ideas?
EDIT to clarify, the razor page you see is a scaffolded page that posts to a seperate JobsController. The data being passed into the post of the 'create' page is working fine, I am only getting the error if the model validation fails.

Mvc ViewBag - Cannot convert null to 'bool' because it is a non-nullable value type

I want to set a bool to true in the controller when producing a certain view and then alter the header of the view accordingly. This should be dead simple but instead Im getting:
Cannot perform runtime binding on a null reference Exception Details:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot perform
runtime binding on a null reference
All I'm doing is in controller:
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.IsRegistration = true;
return View();
}
and then in view:
#if (ViewBag.IsRegistration)
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
but it fails on:
#if (ViewBag.IsRegistration)
UPDATE
Relevant Controller Code:
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.IsRegistration = "true";
return View();
}
The register view:
#model Mvc.Models.RegisterViewModel
#{
Layout = "~/Views/Shared/_AccountLayout.cshtml";
ViewBag.Title = "Register";
}
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
<div class="row">
<div class="col-lg-6">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset class="form-horizontal">
<legend>Create a new account.</legend>
<div class="control-group">
#Html.LabelFor(m => m.UserName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(m => m.UserName)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.Password, new { #class = "control-label" })
<div class="controls">
#Html.PasswordFor(m => m.Password)
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "control-label" })
<div class="controls">
#Html.PasswordFor(m => m.ConfirmPassword)
</div>
</div>
<div class="form-actions no-color">
<input type="submit" value="Register" class="btn" />
</div>
</fieldset>
}
</div>
<div class="col-lg-6"></div>
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList", new { ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The ExternalLoginsList partial:
#using Glimpse.Core.Extensions
#using Microsoft.Owin.Security
#model ICollection<AuthenticationDescription>
#if (Model.Count == 0)
{
<div class="message-info">
<p>There are no external authentication services configured</p>
</div>
}
else
{
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
<fieldset id="socialLoginList">
#if (!string.IsNullOrEmpty(ViewBag.IsRegistration))
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
<p>
#foreach (AuthenticationDescription p in Model) {
<button type="submit" class="btn" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</fieldset>
}
}
Try:
#if (ViewBag.IsRegistration == true)
I know this is an old question, but I think I have an elegant answer, so in case anyone reads this after searching, here is mine:
#if (ViewBag.IsRegistration ?? false)
Try this:
Replace the line in your controller:
ViewBag.IsRegistration = true;
with
ViewBag.IsRegistration = new bool?(true);
and replace the line in your view:
#if (ViewBag.IsRegistration)
with
#if ((ViewBag.IsRegistration as bool?).Value)
Effectively you are putting a nullable bool in the ViewBag and then unwrapping it.
Simply check for null before checking for true:
if (ViewBag.IsRegistration != null && ViewBag.IsRegistration)
Try TempData instead of ViewBag.
Change your code from
Controller
ViewBag.IsRegistration=true;
to
TempData["IsReg"]=true;
and in View
#if((bool)TempData["IsReg"])
It seems that you are using the value in child partial view and you are adding the data in parent action.The values in viewbag cannot pass out data from one action to anothers action's view. While TempData use session it can be used to pass data to one action to another actions view.
Ok so as per Floods suggestion in comments, I need to pass the arguments around. The ViewBag from the parent View does not flow through to partial views.
So in the code for the Register View I needed to change from
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList", new {ReturnUrl = ViewBag.ReturnUrl})
</section>
to
<section id="socialLoginForm">
#Html.Action("ExternalLoginsList",
new {ReturnUrl = ViewBag.ReturnUrl,
IsRegistering = #ViewBag.IsRegistering })
</section>
Then go into my account controller and change from:
[AllowAnonymous]
[ChildActionOnly]
public ActionResult ExternalLoginsList(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return (ActionResult)PartialView("_ExternalLoginsListPartial", new List<AuthenticationDescription>(AuthenticationManager.GetExternalAuthenticationTypes()));
}
to
[AllowAnonymous]
[ChildActionOnly]
public ActionResult ExternalLoginsList(string returnUrl, string isRegistering) {
ViewBag.IsRegistering = (isRegistering.ToLower() == "true");
ViewBag.ReturnUrl = returnUrl;
return (ActionResult)PartialView("_ExternalLoginsListPartial", new List<AuthenticationDescription>(AuthenticationManager.GetExternalAuthenticationTypes()));
}
Then in the ExternalLogins I can just:
#if (ViewBag.IsRegistering)
as necessary.
So Im effectively passing the IsRegistering bool from controller to main view then back up to action method on controller then into ViewBag which allow me to access the bool in the child partial view.
Many thanks.
Booleans in Viewbag are always tricky. Try this instead
[AllowAnonymous]
public ActionResult Register()
{
ViewBag.Registration = "x";//x or whatever
return View();
}
#if (!String.IsNullorEmpty(ViewBag.Registration))
{
<legend>Register using another service.</legend>
}
else
{
<legend>Use another service to log in.</legend>
}
Maybe so:
#if ((ViewBag.IsRegistration != null) && (ViewBag.IsRegistration is bool) && (bool)ViewBag.IsRegistration)
{
}

Viewmodel shows wrong information and how pass a warning

I'm making a register form. On the submit I check if the emailaddress is already registred in the database. If this is the case I redirect to the registerform again with an empty emailaddress.
The form is filled in but the emailaddress is still the one that the user filled in on the first time. How is this possible? Here is some of my code
Razor page
#model LibModels.User
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="row">
#Html.LabelFor(model => model.userEmail)
#Html.EditorFor(model => model.userEmail)
#Html.ValidationMessageFor(model => model.userEmail)
</div> <p>
<input id="knop" type="submit" value="Create" />
</p>
</fieldset>
}
Controller
[HttpPost]
public ActionResult Register(User viewModel)
{
User usr = Adapter.UserRepository.Single(u => u.userEmail.Equals(viewModel.userEmail));
if (usr == null)
{
viewModel.userCreatedDate = DateTime.Now;
Adapter.UserRepository.Insert(viewModel);
Adapter.Save();
}
else
{
viewModel.userEmail = "";
return View(viewModel);
}
return RedirectToAction("Index");
}
It would also be nice to have a warning in the validation that this emailaddress already exists in the database, what is the best way to do this?
I think what you're looking for is
ModelState.AddModelError("userEmail", "Email already exists");
However, I'd suggest looking into creating a custom action filter to do the validation for you. It's a lot cleaner & easy to test.
Here's a tutorial.

Categories