I want to know about a pattern to do this. I have a controller with method1()
method1(){
return View();
}
[httpost]
method1(string something){
return View(object);
}
[httpost]
method2(int? id){
return redirectToaction("method1");
}
View:
<div>
beginform(){
textfield
submit button
}
</div>
<div>
if(viewbag.something != null){
beginform("method2", "controller", new{id=number}){
<span>#model.info</span>
submit button
}
}
</div>
and this displays a view this view has a form a text field and submit and this calls method1() but with the HTTPPOst and in the same view i display another form which will call method2() my question is how can i display a message in the view? like "user has been deleted" without having to create another view. Is there another way of doing this in asp mvc or do i have to include ajax?
I tried setting a viewBag inside method2, but since method2 redirectsaction to method1 it somehow does not stay, and it is not displayed in the view.
Thanks!
You could use TempData which is preserved between a single redirect:
public ActionResult method1()
{
return View();
}
[HttpPost]
public ActionResult method1(string something)
{
return View(something);
}
[HttpPost]
publicActionResult method2(int? id)
{
TempData["message"] = "User has been deleted";
return RedirectToAction("method1");
}
and in the view display the message:
<div>#TempData["message"]</div>
If there is no message in TempData it will simply display an empty div. You could further check if TempData["message"] != null in the view if you didn't want the empty div.
Related
I'm trying this code:-
If no query string supplied to the Index Method then render a Branch Locator View. When a Branch Id is selected in that View, post back to a Redirect To Route Result OR Action Result method and then redirect back to Index with a query string of the selected Branch Id.
I can run through the code successfully without and then with the query string.
I even run through the Index View and can see the Model working however, the Index View does not render, the Branch Selector View remains. Network developer tools shows the correct URL with query string correctly in place when doing the Redirect.
(NOTE: Both methods are on the same controller).
If I add the same query string directly in the Browser address bar it works fine!
I have this code:
[HttpGet]
public ActionResult Index()
{
var querystringbranchId = Request.QueryString["branchId"];
if(!string.IsNullOrEmpty(querystringId))
{
....do stuff like build a model using the branchId...
return View(Model);
}
return View("BranchSelector")
}
[HttpPost]
public RedirectToRouteResult BranchDetails(FormCollection formCollection)
{
var querystringBranchId = formCollection["BranchList"];
var branchId = int.Parse(querystringBranchId);
return RedirectToAction("Index", new { branchId });
}
Try using strongly typed model on the post, and specifying the param as an actual param - Using View models is going to be much better for you.
I have tested the below - It seemed to work as expected for me:
[HttpGet]
public ActionResult Index(int? branchId)
{
if (branchId.HasValue)
{
return View(branchId);
}
return View("BranchSelector");
}
[HttpPost]
public RedirectToRouteResult BranchDetails(MyModel myModel)
{
return RedirectToAction("Index", new { myModel.BranchId });
}
public class MyModel
{
public int BranchId { get; set; }
}
The View:
<div>
#using (Html.BeginForm("BranchDetails", "Home", FormMethod.Post))
{
#Html.TextBox("BranchId","123")
<input type="submit" value="Go"/>
}
</div>
#MichaelLake Thanks to your post I found the problem. I tried your code and sure enough it works as expected. I didn't mention I was using a Kendo Combobox control (!) loaded with the branches. I didn't mention that as the actual data I needed was available in the post method so, thought the issue was with the Controller methods. I had the Kendo control name as BranchList, I changed it to BranchId and it now works with the original code as expected! The Kendo name becomes the element Id and has to match to work.
Many Thanks!
This will work for you. Cheers :D
return RedirectToAction("Index", "ControllerName", new { branchId = branchId});
I have recently taken over support of an ASP.NET MVC project, and am trying to work through some of the errors, one in particular has me stumped though.
We have a 'New' page to add new items, with the following code running when the page is posted:
[HttpPost]
public ActionResult New(RecordView i)
{
if(ModelState.IsValid)
{
repository.AddRecord(i.DogIncident);
return View("Index");
}
return View("Index");
}
However, when it tries to load the Index view, I get the following error: "Object reference not set to an instance of an object." and it points to the following block of code at the top of a file called RecordsView.cshtml:
#for (var i = 0; i < Model.Records.Count; i++)
The record does add correctly though, it just doesn't load the listings page correctly, and since this is just a "nice to have" I thought I'd simplify things by changing it so that it either returns some text which generates an error as it's expecting a boolean returned.
Any ideas on how to fix this please? I'm stumped.
Thanks!
The flow of your code here doesn't look right:
[HttpPost]
public ActionResult New(RecordView i)
{
if(ModelState.IsValid)
{
repository.AddRecord(i.DogIncident);
return View("Index");
}
return View("Index");
}
From your description above, it sounds as though you're POSTing from your New view, to this New action, which should then redirect, when successful, to your Index action. Currently, the above code is not doing that, and it also fails to redisplay the form if the model isn't valid. It should probably look more like this:
[HttpPost]
public ActionResult New(RecordView i)
{
if(ModelState.IsValid)
{
repository.AddRecord(i.DogIncident);
return RedirectToAction("Index");
}
// Redisplay the `New` view, passing the model
// back to it in order to repopulate the form
return View(i);
}
The important distinction between return View(...) and return RedirectToAction(...) is that the latter will run the action and return the associated view. The former will simply return the view. That has implications in that if the Index action builds a model, and passes it to the Index view, none of that will happen if you simply return the view.
You could of course do something like:
return View("Index", new YourModelType());
but that isn't going to work if, as discussed above, your Index action performs some other data construction for your model, such as building drop down lists, which new YourModelType() wouldn't do.
Also, when data that is sent to a POST action is valid, you should be redirecting to another action (as I've done above), rather than simply returning a view, in order to conform with the Post-Redirect-Get pattern, which prevents some types of duplicate form submissions.
You display Index view, and seems that it requires some Model - Model.Records. And you don't pass it in this HttpPost action.
If you have action for that Index page, then you can just do
[HttpPost]
public ActionResult New(RecordView i)
{
if(ModelState.IsValid)
{
repository.AddRecord(i.DogIncident);
return RedirectToAction("Index");
}
return View(i);
}
It will just redirect a user to Index view, after creation of new RecordView item
Basically you may are trying to achieve PRG(Post/Redirect/Get) modal.
I guess, the problem is you are not sending the model for your GET request.
Post--> Save --> Redirect --> Load Data -->Assign to View in Index -->Access in view
//POST & REDIRECT
[HttpPost]
public ActionResult New(RecordView i)
{
if(ModelState.IsValid)
{
repository.AddRecord(i.DogIncident);
return RedirectToAction("Index");
}
}
//GET
public ActionResult Index()
{
var model=new MyViewModel();
model.Records=repository.GetRecords(i.DogIncident);
return View(model); //Assign to View in Index
}
Index.cshtml
#model MyViewModel
#for (var i = 0; i < Model.Records.Count; i++)
If Records is a list, make sure your ViewModel has a constructor:
public class RecordView
{
public List<Record> Records { get; set; }
public RecordView()
{
Records = new List<Record>();
}
}
You mentioned that the record adds correctly, so you must be passing a valid record model into your view from some other action than the one provided.
If #for (var i = 0; i < Model.Records.Count; i++) is the cause of this error, my guess is that the model exists, but the Records property has not been set. One immediate work around would be checking the existence of this property before accessing it. For example:
if (Model.Records != null) {
for (var i = 0; i < Model.Records.Count; i++) .....
}
i think you have collections are not instantiated, the error may be in models not in view models. this because when ever you have a collection you need to instantiate inside of constructor of that entity.
May be this is your answer...!Just look
[HttpPost]
public ActionResult New(RecordView)
{
if(ModelState.IsValid)
{
repositry.AddRecord(i.DogIncident);
return RedirectToAction("Index");
}
}
I have an index action on a controller as follows...
public ActionResult Index(string errorMsg = "")
{
//do stuff
ViewBag.ErrorMsg=erorMsg;
return View();
}
I have another action that is an http post for Index.
When there is something wrong I want to reload the Index page and show the error...
I have my view already conditionally showing errorMsg. But I cannot figure out how to call Index and pass in the error string?
Typically, you'd just share the view between the two actions. I'm guessing you have actions that look something like this (the more info you provide about what index does, the better my example will be):
public ActionResult Index()
{
return View();
}
[HttpPost, ActionName("Index")]
public ActionResult IndexPost()
{
if (!ModelState.IsValid)
{
ViewBag.ErrorMsg = "Your error message"; // i don't know what your error condition is, so I'm just using a typical example, where the model, which you didn't specify in your question, is valid.
}
return View("Index");
}
And Index.cshtml
#if(!string.IsNullOrEmpty(ViewBag.ErrorMsg))
{
#ViewBag.ErrorMsg
}
#using(Html.BeginForm())
{
<!-- your form here. I'll just scaffold the editor since I don't know what your view model is -->
#Html.EditorForModel()
<button type="Submit">Submit</button>
}
If I understand you correctly you just need to hit the url with the errorMsg in the query string:
/*controllername*/index?errorMsg=*errormessage*
However, when there is something wrong you don't necessarily need to reload the page. Seems like you might be approaching this in the wrong way..?
You can use RedirectToAction to redirect to the page, with a querystring for errorMsg value.
[HttpPost]
public ActionResult Index(YourViewModel model)
{
try
{
//try to save and then redirect (PRG pattern)
}
catch(Exception ex)
{
//Make sure you log the error message for future analysis
return RedirectToAction("Index",new { errorMs="something"}
}
}
RedirectToAction issues a GET request. So your form values will be gone, because HTTP is stateless. If you want to keep the form values as it is in the form, return the posted viewmodel object again. I would get rid of ViewBag and add a new property called ErrorMsg to my ViewModel and set the value of that.
[HttpPost]
public ActionResult Index(YourViewModel model)
{
try
{
//try to save and then redirect (PRG pattern)
}
catch(Exception ex)
{
//Make sure you log the error message for future analysis
model.ErrorMsg="some error";
return View(model);
}
}
and in the view you can check this model property and show the message to user.
After form submit Html editor helpers (TextBox, Editor, TextArea) display old value not a current value of model.text
Display helpers (Display, DisplayText) display proper value.
Is there any way editor helpers to display current model.text value?
Model
namespace TestProject.Models
{
public class FormField
{
public string text { get;set; }
}
}
Controller
using System.Web.Mvc;
namespace TestProject.Controllers
{
public class FormFieldController : Controller
{
public ActionResult Index (Models.FormField model=null)
{
model.text += "_changed";
return View(model);
}
}
}
View
#model TestProject.Models.FormField
#using (Html.BeginForm()){
<div>
#Html.DisplayFor(m => m.text)
</div>
<div>
#Html.TextBoxFor(m => m.text)
</div>
<input type="submit" />
}
When you submit the form to an MVC action the values of the input fields are recovered from the POSTEd values available in the form and not from the model. That makes sense right? We don't want the user to show a different value in a textbox than they have just entered and submitted to the server.
If you want to show the updated model to the user then you should have another action and from the post action you have to redirect to that action.
Basically you should have two actions one action that renders the view to edit the model and another one saves the model to database or whatever and redirect the request to the former action.
An example:
public class FormFieldController : Controller
{
// action returns a view to edit the model
public ActionResult Edit(int id)
{
var model = .. get from db based on id
return View(model);
}
// action saves the updated model and redirects to the above action
// again for editing the model
[HttpPost]
public ActionResult Edit(SomeModel model)
{
// save to db
return RedirectToAction("Edit");
}
}
When using HTML editors such as HTML.EditorFor() or HTML.DisplayFor(), if you attempt to modify or change the model values in the controller action you won't see any change unless you remove the ModelState for the model property you want to change.
While #Mark is correct, you don't have to have a separate controller action (but you usually would want to) and you don't need to redirect to the original action.
e.g. - call ModelState.Remove(modelPropertyName)...
public ActionResult Index (Models.FormField model=null)
{
ModelState.Remove("text");
model.text += "_changed";
return View(model);
}
And if you want to have separate actions for GET and POST (recommended) you can do...
public ActionResult Index ()
{
Models.FormField model = new Models.FormField(); // or get from database etc.
// set up your model defaults, etc. here if needed
return View(model);
}
[HttpPost] // Attribute means this action method will be used when the form is posted
public ActionResult Index (Models.FormField model)
{
// Validate your model etc. here if needed ...
ModelState.Remove("text"); // Remove the ModelState so that Html Editors etc. will update
model.text += "_changed"; // Make any changes we want
return View(model);
}
I had some similar problem, I hope I can help others have similar problem:
ActionExecutingContext has Controller.ViewData.
as you can see:
new ActionExecutingContext().Controller.ViewData
This ViewData contains ModelState and Model. The ModelState shows the state of model has passed to controller for example. When you have an error on ModelState the unacceptable Model and its state passed to View. So you will see the old value, yet. Then you have to change the Model value of ModelState manually.
for example for clearing a data:
ModelState.SetModelValue("MyDateTime", new ValueProviderResult("", "", CultureInfo.CurrentCulture));
Also you can manipulate the ViewData, as here.
The EditorFor, DisplayFor() and etc, use this ViewData contents.
I have a contact page and this page shall either show a form or a success message or a failure message, so basically something like this:
#model MyApp.Models.ContactData
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
...Some static content...
If page was opened the first time
-> Render a form here
Else If form was posted and data successfully processed
-> Render a success message here
Else If form was posted but error occurred during processing
-> Render a failure message here
...Some static content...
</div>
I don't know what's the best way to achieve this with MVC 3. Do I create three completely separate views (which is something I'd like to avoid because of the static content which would be the same for all three views)? Or could I create three partial views and then decide based on an additional flag I could put into the model class which partial view to render? Or can I inject somehow the partial views dynamically from the controller into the view?
The controller I have so far looks like this:
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return ?; // What do I return now? It must somehow depend on result.
}
else
return View(contactData));
}
}
I had a similar page and behaviour with ASP.NET WebForms and the solution was there to put the three variable blocks of markup into asp:Panel controls and then switch on or off the Visible flag of those panels from code-behind. I guess I need quite another approach with ASP.NET MVC to reach the same goal.
What is the best way?
Thank you for suggestions in advance!
You can try this way:
[HttpPost]
public ActionResult Index(Contact contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
if (service.Process(contactData))
{
TempData["Success"] = "Your success message.";
return RedirectToAction("Index");
}
else
{
TempData["Error"] = "Your fail message.";
}
}
return View(contact);
}
Perhaps use the ViewBag to help achieve all this. Of course it's a dynamic, so you can add & check for any prop you want/need/expect.
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
ViewBag.ContactSuccess = true;
}
else
{
ViewBag.ModelStateErr= "some err";
}
return View(contactData));
}
Then in your View:
if (ViewBag.ContactSuccess !=null && ((bool)ViewBag.ContactSuccess))
{
//thanks for posting!
}
else
{
if (ViewBag.ModelStateErr !=null)
{
//show that we have an err
}
else
{
//we have no err nor a 'true' contact success yet
//write out the form
}
}
Looks like that you can issue an ajax call on the client side, and based on the Json result, you can render different content from the client side.
I'd suggest coding up three different Views
index.cshtml
contactSuccess.cshtml
contactFail.cshtml
Then in your Controller, you'll have similar code as before
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return View("contactSuccess.cshtml");
}
else
return View("contactFail.cshtml", contactData);
}
}
This way each view has an independent and you don't have a big inline IF block in the middle of your markup.
Alternatively (and this is how I'd do it) you can have the index.cshtml contain three partials...
_ContactForm.cshtml
_ContactSuccess.cshtml
_ContactFail.cshtml
and then you can load the partial views into the index view, and even swap them out dynamically using AJAX.