I have an HttpPost method that add to the database
public ActionResult SubmitData(MyViewModel model)
{
if (ModelState.IsValid)
{
var result = submitData(command);
if (response.success)
{
return RedirectToAction("MyHttpGetMethod");
}
}
return View("MyHttpGetMethod", model);
}
public ActionResult MyHttpGetMethod(int Id)
{
MyModel model = GetData(Id);
return View(model);
}
After the HttpPost is called and the database changes are made successfully, I redirect to
the HttpGet action to get the current data after the changes. I like to display a success message
on the View. I can't use ViewBag because of the Redirect and I can't use TempData, because it's not recommended here.
TempData is a valid solution for this specific use case. But if you do not prefer to do that, you can pass a querystring value to indicate the status of the transaction from your HttpPost action to the GET action.
So update your GET action to have another paramter
public ActionResult MyHttpGetMethod(int Id,string m="")
{
MyModel model = GetData(Id);
if(!String.IsNullOrEmpty(m) && m=="s")
{
// do some code to show the success message here.
ViewBag.Msg="Saved Successfully";
}
return View(model);
}
and in your view, use the ViewBag item to show the message (with whatever styles you want)
<p>#ViewBag.Msg</p>
and in your HttpPost action,
return RedirectToAction("MyHttpGetMethod", new { id =model.Id, m="s"} );
Related
I'm new to MVC and trying to create a wizard-style series of views, passing the same model instance from one view to the next, where the user completes a little more information on each form. The controller looks something like this:-
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return View("Step3", model);
}
// etc..
Questions:-
When I submit the form from the Step1 view, it calls the Step1 POST method and results in the Step2 view being displayed in the browser. When I submit the form on this view, it calls the Step1 POST method again! I got it to work by specifying the action and controller name in Html.BeginForm(), so I'm guessing that the parameterless overload just POSTs back to the action that rendered the view?
I've noticed that the browser's address bar is out of sync with the current view - when I'm on the Step2 view it still shows the Step1 URL, and when on Step3 it shows the Step2 URL. What's going on?
Another approach I've seen for passing a model between views is to put the model in TempData then use RedirectToAction(). What are the pros and cons of this method versus what I'm currently doing?
I won't be providing any "back" buttons of my own in the wizard. Are there any pitfalls to be aware of regarding the browser's back button, and do either of the above two approaches help (or hinder)?
Edit
Prompted by #StephenMuecke's comment I've now rewritten this to use a single view. I tried this once before but had difficulties round-tripping a "step number" to keep track of where I was in the wizard. I was originally using a hidden field created with #Html.HiddenFor', but this wasn't updating as the underlying model property changed. This appears to be "by design", and the workaround is to create the hidden field using vanilla HTML (
Anyway the one-view wizard is now working. The only problem is the old chestnut of the user being able to click the back button after they have completed the wizard, make a change, and resubmit a second time (resulting in a second DB record).
I've tried adding [OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] to my POST method, but all this does is display (in my case) a Chrome error page suggesting that the user clicks refresh to resubmit the form. This isn't user friendly and doesn't prevent a second submit.
you can use RedirectToAction() in this case without worrying about TempData. Just add your model as a parameter to each action and use RedirectToAction("Step2", model);
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step2", model);
}
[HttpGet]
public ActionResult Step2(MyModel model)
{
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
return RedirectToAction("Step3", model);
}
// etc..
The answer to #1 is found in #2.. if you dont specify the Action in you Html.BeginForm() it posts to the current url.
Using TempData to avoid model displaying in url.
[HttpGet]
public ActionResult Step1()
{
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step2");
}
[HttpGet]
public ActionResult Step2()
{
var model = TempData["myModel"] as MyModel;
return View(model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
return View(model);
TempData["myModel"] = model;
return RedirectToAction("Step3");
}
// etc..
Another option would be to add the name of the next action to ViewBag and set your actionName in each BeginForm()
[HttpGet]
public ActionResult Step1()
{
ViewBag.NextStep = "Step1";
return View();
}
[HttpPost]
public ActionResult Step1(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step1";
return View(model);
}
ViewBag.NextStep = "Step2";
return View("Step2", model);
}
[HttpPost]
public ActionResult Step2(MyModel model)
{
if (!ModelState.IsValid)
{
ViewBag.NextStep = "Step2";
return View(model);
}
ViewBag.NextStep = "Step3";
return View("Step3", model);
}
//View
#using (Html.BeginForm((string)ViewBag.NextStep, "ControllerName"))
{
}
I'd prefer to add NextStep as a property to MyModel and using that instead of using ViewBag though.
I understand the thought behind your approach and don't have any issues with it. Unfortunately, I don't believe that ASP.NET MVC is geared very well for passing the the same view model (with data!) between different actions.
Typically, the scaffolded actions in the controller will either create a model item or find it by identifier in the database.
I don't know if this would help, but you could try to save it to the database on every step, and then retrieve it by identifier, or you could also save it to a session and grab it that way.
One issue I do see with your approach is you have Step2 set as a get, yet you probably want to post data to it from Step1 instead of using a query string. You may need to reconcile that issue.
I need to pass data to the action method below from another action method or another view. Then it will be used where the arrow points. But this action method already has a parameter and get value from its own view. And it mustn't have more than one parameter. I don't know how.
Try to use TempData like below examples:
pass data from action to another action
public ActionResult Action_A()
{
TempData["Data"] = "ABC";
return View();
}
public ActionResult Action_B()
{
var data = TempData["Data"];
return View();
}
pass data from action view to another action
Action View
#{
TempData["Data"] = "hello";
}
<p>This is a Demo</p>
Another Action
public ActionResult Action_B()
{
var data = TempData["Data"];
return View();
}
How do I retrieve the values in the Verify View that I am passing in the Create View. I want to display these values from the cshtml file.
[HttpPost]
public ActionResult Create(Ticket ticket)
{
return RedirectToAction("Verify", ticket);
}
[HttpGet]
public ActionResult Verify()
{
return View();
}
</div>
</body>
Try setting the value in TempData and access it in the verify action and pass it over to your verify view and now you can access the Ticket model in verify view. TempData is persisted for the subsequent request. RedirectToAction infact does a 302 request to the browser which in turn redirects to the Verify action and you will get the ticket from TempData there.
[HttpPost]
public ActionResult Create(Ticket ticket)
{
TempData["ticket"] = ticket;
return RedirectToAction("Verify");
}
[HttpGet]
public ActionResult Verify()
{
Ticket ticket = (Ticket)TempData["ticket"];
//Do something
return View(ticket);
}
I don't think it is a good idea to set a complex type in RouteDataDictionary.
So I have a simple action in my controller. The project is a MVC Mobile Application.
public ActionResult Index()
{
return View();
}
this gives an form to enter data. I then handle the data in the post back.
[HttpPost]
public ActionResult Index(ScanViewModel model)
{
if (ModelState.IsValid)
{
Scan ns = new Scan();
ns.Location = model.Location;
ns.Quantity = model.Quantity;
ns.ScanCode = model.ScanCode;
ns.Scanner = User.Identity.Name;
ns.ScanTime = DateTime.Now;
_db.Scans.Add(ns);
_db.SaveChanges();
}
return View(model);
}
I want to clear the fields in the form and allow users to enter data again. However I get the exact same values back into my inputs. How can I clear them in the controller.
Just call this.ModelState.Clear()
You should follow the PRG pattern.
Just redirect to the Action method which is meant for the Create Screen. You can use the RedirectToAction method to do so.
RedirectToAction returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
[HttpPost]
public ActionResult Index(ScanViewModel model)
{
if(ModelState.IsValid)
{
//Code for save here
//..............
_db.SaveChanges();
return RedirectToAction("Index","User");
}
return View(model);
}
public ActionResult Index()
{
return View();
}
Assuming your controller name is UserController.
I have a registration form in the side bar of my web application. When the user submits the entered data, the user should be redirected to another page with a more complete registration form when he can fill the rest of the data. The data that was entered by the user in the first form should be already there in the second form, but that's not happening... I checked to see the value of the view model I'm passing to the second action method and it was null and in the browser's address bar I get:
http://localhost:2732/User/RegisterPage?model=Sharwe.MVC.ViewModels.RegisterPageViewModel
Here's the code:
public ActionResult Register()
{
return PartialView(new RegisterViewModel());
}
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RedirectToAction("RegisterPage", "User", new { viewModel = model });
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}
Isn't that the way to do this? Or am I missing something here...?
The Dictionary passed to RedirectToAction() is the Route Value not the View Model. And RedirectToAction() is basically telling the browser to go to a certain URL. Browser by default makes the GET request and obviously you lose your data.
For this, you need to use TempData dictionary. You can store view model in TempData and then RedirectToAction() to RegisterPage. TempData saves the data for only 1 request span and would delete it automatically. It's ideal for this scenario.
See this for more details > The value for a object inside a viewmodel lost on redirect to action in asp.net mvc 2.0?
In this particular case you don't need to use RedirectToAction, you can simply call the RegisterPage action directly:
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RegisterPage(model);
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}