Let's say I populate a viewbag object in my page action result like this
public ActionResult MiEmployeeDetails()
{
var employees = _db.geo_employees.Select(x => new SelectListItem
{
Value = x.name,
Text = x.name
}).ToList();
ViewBag.Employees = employees;
return View();
}
and then populate in my view as such
<%= Html.DropDownList("Employees") %>
That's fine but then when I hit the submit button and it goes to Httppost Action Result i.e.
[HttpPost]
public ActionResult MiEmployeeDetails(FormCollection fc)
{
return View();
}
I get a null reference exception on my viewbag option. So in my Httppost action result do I also need to reset the viewbag object created in my original page load action result ?
Each request is completely independent. You need to reinitialize all view data if you call the view again from a post. The only exemption is data that is sent from the Client during the post
Related
I have an application where I need to input data and on submit the data gets saved in database. When I checked in database the input is getting saved successfully but I am getting an exception when the page reloads after httppost.
I am getting exception at :
#Html.DropDownList("LineID", new SelectList(Model.dropConfig, "LineID", "LineID"), "-- Select LineID --", new { required = true, #class = "form-control" })
controller code to get the dropdownlist values, binding with Db:
[ActionName("DetailsForm")]
[HttpGet]
public ActionResult DetailsForm()
{
try
{
var model = new DetailsViewModel() { dropConfig = floorService.DropDownList().ToList() };
return View("DetailsForm", model);
}
catch (Exception ex)
{
return View("_error");
}
}
controller code to http post:
[ActionName("DetailsForm")]
[HttpPost]
public ActionResult DetailsForm(DetailsViewModel model, FormCollection form)
{
DetailsConfiguration detailsConfig = new DetailsConfiguration();
detailsConfig.LineID = Convert.ToString(form["LineID"]);
//Similary for other fields
floorService.SaveDetails(detailsConfig);
ModelState.Clear();
ViewBag.message = "Success";
return View("DetailsForm",model);
}
Snapshot of exception:
Because your view code is using Model.dropConfig to build the SelectList for your dropdown, and you are not setting the dropConfig property value before returning to the view.
Remember, Http is stateless. So even though you set the dropConfig property value in the GET action, It won't be available inside your HttpPost action. When you submit your form, it is a totally new request to the server.
You can fix it by loading dropConfig property again.
model.dropConfig = floorService.DropDownList().ToList();
return View(model);
But ideally you should be following the P-R-G pattern.
P-R-G stands for Post-Redirect-Get. So when you submit your form to an http post action method, you should return a redirect response and the browser will make a new GET call to that action method.
You can use the RedirectToAction method to return a redirect response.
floorService.SaveDetails(detailsConfig);
return RedirectToAction("DetailsForm");
This will send a 302 response back to the browser with the location header set to the url to the DetailsForm action method and the browser will make a new GET request to that.
ViewBag won't work when with redirect response. So you might consider using TempData. TempData can be used to transfer between two requests.
TempData["message"] = "Success";
return RedirectToAction("DetailsForm");
Now you can read TempData["message"] in the DetailsForm action method or the view rendered by that.
For example, you can read it in the view (rendered by DetailsForm GET action method) like this
#if (TempData["message"]!=null)
{
<div class="alert alert-success" id="alert">
<button type="button" class="close" data-dismiss="alert">x</button>
<strong>Success! </strong>#TempData["message"]
</div>
}
I have created a page that takes in a string that will search a list of vendors. My goal is to output them to a grid list on an HTML page. Oddly enough, the first page loads, and I can break point the code until the return view of the actual list page. However, it never actually loads. It is even more frustrating because if I don't pass the model to the grid page, it gives me the typical "You can't use a null model", but then it still doesn't load the new page. I have tried several versions. The most current is below.
[HttpPost]
public ActionResult Search(String searchString)
{
this.searchString = searchString;
List<VendorInvoice> v = VendorSearches.publicSearch(searchString);
test = v;
ViewData.Model = v;
TempData.Add("test",v);
return RedirectToAction("Search");
}
[HttpGet]
public ActionResult Search()
{
List<VendorInvoice> v = (List<VendorInvoice>)TempData["test"];
return View("Search",v);
}
So if I take the v out, then I get the error about not passing the model. If it is there, then nothing will happen. The new page won't load.
In your HttpPost search action method, you are setting the result data to show in TempData and doing calling the RedirectToAction method.
RedirectToAction returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. That means, it will be totally new request coming to your search GET action again. Since Http is stateles, it does not have any idea about what you did in your previous request. The data stored in TempData won't be available to this request.
What you should be doing is, similar to your GET action method, simply
return the result to the view.
[HttpPost]
public ActionResult Search(String searchString)
{
this.searchString = searchString;
List<VendorInvoice> v = VendorSearches.publicSearch(searchString);
return View("Search",v);
}
That should fix your problem. But as Stephen Muecke mentioned, you can keep just your GET action method for your Initial view and search result view
public ActionResult Search(String searchString="")
{
List<VendorInvoice> v = new List<VendorInvoice>();
v = VendorSearches.publicSearch(searchString);
return View("Search",v);
}
And your view
#model List<VendorInvoice>
#using(Html.BeginForm("Search","YourControllerName",FormMethod.GET)
{
<input type="text" name="searchString" />
<input type="submit" />
}
<h2>Results</h2>
#foreach(var item in Model)
{
<p> #item.SomePropertyNameOfYourVendorInvoiceHere </p>
}
I am storing a value in dynamic property called login in my Login action method in my controller and I am accessing the value of the viewbag in Index view. I am gettng the value as null. Why is this so.?
Following is my code which is in controllers Login action method.
ViewBag.Login = "false";
return RedirectToAction("Index");
here is my code which I am using in the Index view(cshtml).
#if (#ViewBag.Login != "")
Here in view I am getting #ViewBag.Login's value as null. Even if i remove the # symbol like this
ViewBag.Login Still I get value as null.
Please help. ViewBag should persist value within view's and action methods which are bind to same controller.
ViewBag does not persist across http requests.
You could do
public ActionResult Login()
{
/* Pass `Login` by QueryString */
return RedirectToAction("Index", new { Login = false });
}
public ActionResult Index(bool Login)
{
/* Read from QueryString, and pass the value to `ViewBag` */
ViewBag.Login = Login;
return View();
}
The ViewBag won't survive across a RedirectToAction. You can use TempData, which can be accessed exactly once:
TempData["Login"] = "false";
return RedirectToAction("Index");
I have the following code in Edit get method.
public async Task<ActionResult> Edit(int? id)
{
Event e = await db.Events.FindAsync(id);
ViewBag.OldCategories = e.Categories.ToList();
// ViewBag.OldCategories is not null (tested in VS quick viewer)
And I have the following code in the post method.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,....,Categories")] EventCreateViewModel eventVm)
{
var oldCategories = ViewBag.OldCategories; // Null
However, the oldCategories is always null in the post method. Did I miss anything?
ViewBag is only accessible in View when you set it from controller, when form is posted ViewBag values are removed, if you want the values to be accessible in form post you can post them via hidden fields.
<input type="hidden" name="MyHidden" value="#ViewBag.AnyKey"/>
or other way around is to use TempData, instead of placing in viewbag, place data in TempData and remember TempData is once read, means once you read its value it will be removed from TempData, you can retain it by calling TempData.Keep()
public ActionResult MyAction()
{
TempData["myKey"] = "";
}
and View:
#{
string value = TempData["myKey"] as stirng;
TempData.Keep("myKey") // retains value in TempData
}
Once the View is rendered with Razor, the ViewBag is emptied. It's only good for one trip to the client, and then it's wiped. If you want to be able to read it back in your Controller when the client calls back to the server, you'll need to use TempData instead.
Even if you do a Redirect() inside a Controller Action, your ViewBag data will be lost, because you're moving onto a second page, and out of the scope of ViewBag.
I have this problem:
I go to a page such as:
/Auction/Details/37
and this calls this action method:
public ActionResult Details(int id)
A particular line in this method is:
return View("DetailsLub", auction);
This view contains this line:
#Html.Action("BidOnAuction", new { auctionId = Model.Id })
Which calls this action method:
public PartialViewResult BidOnAuction(int auctionId)
So far so good?
Now, I have a form in the BidOnAuction view, whcih has a button. When I click on this button, this action method is invloked:
[HttpPost]
public ActionResult BidOnAuction(BidOnAuctionViewModel model)
This action method has a catch statement with the following lines:
ModelState.AddModelError(string.Empty, operation + #" Failure: " + message);
return RedirectToAction("Details", new { id = model.AuctionId });
Now, both the DetailsLUB view and the BidOnAction view contain this line:
#Html.ValidationSummary(true)
But, the issue is that nothing ever gets printed to the screen. What am I doing wrong?
InOrder to get the validation Message on the page you need to return view with Model, as model has the Model State within it, something like this:
return View(Model);
This will return the model BidOnAuction with Validation Summary.
This line of code
return RedirectToAction("Details", new { id = model.AuctionId });
Returns instance of RedirectResult class. That is generally used for redirections and does not render view. If you want to render child action into parent view using #Html.Action, you need to return view from that child action, not RedirectResult. And that RedirectResult will not work even when there's no child action. Returning RedirectResult causes browser to issue fresh, all new request to that action. And model state is lost anyways. You should do something like
try
{
//some actions
return RedirectResult("Details", new { id = model.AuctionId });
}
catch
{
ModelState.AddModelError(string.Empty, operation + #" Failure: " + message);
return View("Details", new { id = model.AuctionId });
}
You can't redirect to a new action and expect the modelstate to be there.
If the modelState is invalid just return (with View(model))
else
redirect to details.
If you need the error information in the details view you will have add it to TempData or pass it in as an optional parameter.