model data disappears between controllers - c#

So i have a model with two models in it so that i can render to different views on the same view, one login view and one register view.
This is my model:
public class SignUpLoginModel
{
public LoginModel loginmodel { get; set; }
public RegisterModel registermodel { get; set; }
}
And when i am trying to register a new user with this controller method:
public ActionResult Register(SignUpLoginModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.registermodel.UserName,
model.registermodel.Password,
new
{
FirstName = model.registermodel.Firstname,
LastName = model.registermodel.Lastname,
Country = model.registermodel.Country,
City = model.registermodel.City,
Birthdate = model.registermodel.Birthdate
});
WebSecurity.Login(model.registermodel.UserName, model.registermodel.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return RedirectToAction("Register", "Home", model);
}
So the modelstate.isvalid failes as it should and it redirects to the home controller and the register method and send the model. Here everything works fine, the model contains the data. But when i redirect to another controller
public ActionResult Register(SignUpLoginModel model)
{
ViewBag.Message = "RegisterFail";
return View("Index", "Home", model);
}
my model ends up with null instead of the right data. What am i doing wrong?

I'm pretty sure it's because RedirectToAction (neither the overload you are using nor any of the other signatures) doesn't forward your model for you. It sends a 302 back to your browser which then issues a new GET request to a new url - therefore your model data is lost.
So you'd need to do something like this in the first controller
{
TempData["signupModel"]=model;
return RedirectToAction("Register", "Home", model);
}
and then this in the redirect controller:
public ActionResult Register(SignUpLoginModel model)
{
ViewBag.Message = "RegisterFail";
return View("Index", "Home", TempData["signupModel"]);
}
But what I don't understand is why you don't just directly return a view the first time around, why do you need to redirect - because from your code you seem to be just redirecting back to the same controller - or have I missed something?
Edit based on comments below
I honestly think you would be better off using Ajax for this because what you're trying to do here is maintain state across multiple controllers/views which is not really idiomatic for MVC.
If you submit the "register" popup form via Ajax, then you never actually leave the Index page, you just execute some code server-side and return some JSON to indicate if your registration was OK or not. When this response indicates an error you can just display an error message on the popup form or whatever, otherwise you can redirect to wherever on the client-side.
OR
When your Register controller decides that registration has failed, return the Home page index view e.g
ViewBag.Message = "RegisterFail";
return View("~/HomePage/HomeController/Index.cshtml", model); //or whatever the virtual path to the home page view is
However I don't like this approach as it sounds to me like you have your register form HTML being generated in-line by the the Home page view, which sounds like mixed concerns to me.

Related

View not found from controller

I return the View from my controller start_run1 to a view start_run1
public ActionResult start_run1(int? id) { ....
return(View);
}
I get 404 page not found, even if I try using absolute paths etc etc.
if I add the code it finds the page but model data or ViewBag data doesn't get passed.
public IActionResult start_run1()
{
return View();
}
I have another similar controller function which works without doing this.
Any ideas, Ive tried everything.

Controller redirection doesn't change view

I'm doing a simple RedirectAction in my controler and in this new Controller i'm calling the new View, however in the Browser the View is not changing, i can see in the cshtml the code getting there, but i don't know what i'm missing.
public ActionResult ExecuteBreakdown(string param1)
{
return RedirectToAction("ShowMatrix", "BreakdownMatrix", new { param = param1 });
}
public ActionResult ShowMatrix(string param1 )
{
...lots of code
return View("ShowMatrix", priceMatrix);
}
I'm not being redirect to the ShowMatrix View.
You need to return something like a PartialView in your redirect to action call. However, the call needs to be scoped to a parent view that can display the partial properly. You can't just load what you send over like that.

Call controller from another controller and return a view

I call an Action from a Login controller to authenticate users, once the user is authenticated I would like to call either the Cashier or the Supervisor action, depending on the user's role, and display the appropriate view.
I can break on AuthenticateUserByCard but RedirectToAction doesn't seem to be working.
I'm not sure if what I'm trying to do is deviating from the MVC architecture, if so please suggest the correct way to do this
Login controller:
public class LoginController : Controller
{
public ViewResult Index()
{
return View();
}
[HttpPost]
public ActionResult AuthenticateUserByCard(string token)
{
//Authenticate user and redirect to a specific view based on the user role
Role role = GetRoleByToken(token);
if(role.UserType == UserType.Supervisor)
return RedirectToAction("Supervisor", "Login", new { id = token });
else
return RedirectToAction("Cashier", "Login", new { id = token });
return null;
}
public ActionResult Supervisor(string id)
{
//Do some processing and display the Supervisor View
return View();
}
public ActionResult Cashier(string id)
{
//Do some processing and display the Cashier View
return View();
}
}
Java Script:
$.get("/Login/AuthenticateUserByCard",{token:token});
jQuery post and get ignore 301 browser redirects returned from the server. You would normally need to handle them yourself. This can get messy: How to manage a redirect request after a jQuery Ajax call
All you really need in this case is to return the choice of methods, but make them return explicit views (not implicit). The default would always be to return the view based on the IIS-called method i.e. "AuthenticateUserByCard" unless you specify the view.
e.g.
public class LoginController : Controller
{
public ViewResult Index()
{
return View();
}
[HttpPost]
public ActionResult AuthenticateUserByCard(string token)
{
//Authenticate user and redirect to a specific view based on the user role
Role role = GetRoleByToken(token);
if(role.UserType == UserType.Supervisor)
return Supervisor(token);
else
return Cashier(token);
return null;
}
public ActionResult Supervisor(string id)
{
//Do some processing and display the Supervisor View
return View("Supervisor");
}
public ActionResult Cashier(string id)
{
//Do some processing and display the Cashier View
return View("Cashier");
}
This will not change the URL though. If you need that too try the other answer I linked. You basically handle the redirect in jQuery and goto the new page.
Alternatively, to change the URL, put the desired URL into a hidden field of the returned views and extract that value to update the browser URL (just a thought) :)

How to use RedirectToAction while passing view model to a view with different route

I have a ASP.NET MVC 3 app with a common scenario where there is with an "About" controller using "Contact" and "ThankYou" actions.
I want user to go to /about/contact, fill out the form, submit it, and be taken to /about/contact/thankyou * page where the form contents (aka view model) will be displayed.
** Note there is no "Contact" controller nor do I want to create one for this purpose if possible.*
I am using RedirectToAction to prevent resubmission (Post/Redirect/Get pattern) and TempData to pass on view model to thank you page.
I can also use TempData to check if thank you page was reached directly and redirect back to contact form page (so it would not register as a "goal" in web analytics)
But one thing I could not figure out is how to use different route for thank you page so it appears as /about/contact/thankyou
Am I doing this right?
Is there a better way?
Here are relevant actions in AboutController
<!-- language: c# -->
[RequireHttps]
public ActionResult Contact()
{
var viewModel = new ContactViewModel();
return View(viewModel);
}
[RequireHttps]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(ContactViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
// not sure if this is the best way or how to redirect to /contact/thankyou
TempData["viewModel"] = viewModel;
return RedirectToAction("ThankYou");
}
[RequireHttps]
public ActionResult ThankYou()
{
var viewModel = TempData["viewModel"];
// upon direct access, viewModel will be null and missing, so redirect to Contact form
if (viewModel == null)
{
return RedirectToAction("Contact");
}
return View(viewModel);
}
You could define a custom route before the default route:
routes.MapRoute(
"contact",
"about/contact/thankyou",
new { controller = "About", action = "ThankYou" }
);
Now when you navigate to /about/contact the Contact GET action will be executed (You have to rename it as in your code it is called ContactUs). Then the user fills the form and submits to the Contact POST action. The url stays the same for the moment: /about/contact. After the redirect the ThankYou action is executed and the url changes to /about/contact/thankyou.

Can I share a view in asp.net mvc?

My controller has 2 actions:
Results()
Index()
I want to share the view named index.aspx between these 2 actions.
See my previous post for more information
When I build my link to the page, I assume I cannot send it to Index action as it is expecting a FormCollection type and hence I create a Results action
public ActionResult Results(ClientSearch data, int? page)
{
FormCollection collection = new FormCollection();
collection.Add("FNAme", data.FName);
collection.Add("Lane", data.Lane);
collection.Add("Zip", data.Zip);
collection.Add("Phone", data.Phone);
return Index(page, collection);
}
Not sure I completely understand your question, but if you want to use the same View on different ActionResults, you can:
public ActionResult One() {
// do stuff
return View("Index", myModel);
}
public ActionResult Two() {
// do stuff
return View("Index", myOtherModel); // Same View
}
Just make sure you are providing the same Type for the View (if the View needs a Type at all).
Of course you can. It's up to controller to decide how to react and what view to serve back.
Now that I've read your question to the end :)), well, you can get away with two actions of the same name. The one will be accepting GET commands (initial load of the page), the other will be serving POST requests, perform the necessary action and redirect back to the same View.
public MyController
{
[AcceptVerbs (HttpVerbs.Get)]
public ActionResult Index ()
{
return View ();
}
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Index (ClientSearch data, int? page)
{
// Process form post
return RedirectToAction ("Index");
}
}

Categories

Resources