Search page doesn't show up following HttpGet request MVC c# - c#

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>
}

Related

System.ArgumentNullException occured in System.Web.Mvc.dll

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>
}

GET method executing before POST method after a page submit?

After editing a form and clicking a Save button, the HttpGet method is being executed before the HttpPost method. The page is reloading with the query string in the URL, and the old data still populating the fields, but the data has been saved on the server side. If I remove the query string and reload the page, the new data appears.
My expectation is that only the HttpPost method would be called, changes would be saved saved, then the page would be loaded back up with the saved changes.
Using the Microsoft.AspNetCore.Mvc": "1.0.0 package.
Here are my HttpGet and HttpPost methods:
[HttpGet]
[Route("~/Home/Activity/{activityId}")]
public IActionResult Activity(int activityId)
{
ViewData["Title"] = "Activity Detail";
FundraiserDBContext context = new FundraiserDBContext(_ServerName, EnvironmentCode);
Engagement activity;
if (activityId == -1)
{
activity = new Engagement();
context.Engagement.Add(activity);
}
else
{
activity = context.Engagement.FirstOrDefault(a => a.Id == activityId);
}
if (activity != null)
{
ActivityViewModel vmActivity = new ActivityViewModel(activity, context);
return View("Activity", vmActivity);
}
else
{
ActivityViewModel vmActivity = new ActivityViewModel(context);
return View("Activity", vmActivity);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
//[Route("~/Home/Activity/{activityId}")]
public IActionResult Activity(ActivityViewModel vmActivity)
{
FundraiserDBContext db = new FundraiserDBContext(_ServerName, EnvironmentCode);
if (ModelState.IsValid)
{
db.Engagement.Update(vmActivity.ToEngagement(db));
db.SaveChanges();
}
return View("Activity", vmActivity); //this was vm.EngagementId
}
And here is the code for the Save button:
<button type="submit" class="btn-success pull-right" style="width:80px;" onclick="location.href='#Url.Action("Activity", "Home", #Model)'">Save</button>
Remove redirect from post method, because before returning the View its redirecting to the Index method without updated model
Redirect($"~/Home/Index"); // remove this line
Matjaž Mav found my error and described it in the comment below the original post. I mistakenly thought I needed the onclick event on my button. Removing this resulted in the expected behavior I was looking for.
The button code now looks like this:
<button type="submit" class="btn-success pull-right" style="width:80px;">Save</button>

Returning JSONResult without switching views

I want to return a JSON result. To do this I have a controller method as follows that is called from a Ajax.BeginForm on the View:
#using (Ajax.BeginForm("Update", new AjaxOptions { OnSuccess = "MySuccessMethod()" }))
{
<!-- some form stuff -->
<input type="submit" value="Submit"/>
}
This is the controller that handles it:
[HttpPost]
public JsonResult Update(FormCollection fc)
{
// Process form stuff
return Json (new {success = true });
}
What I want is to process the success response with MySuccessMethod. What I see is that the view on submit goes to the correct controller method above, which then redirects the page to the URL /Home/Update with the following string in the screen:
{"success": true }
Not sure if it is relevant but I am using Mono.
How can I make the framework not switch pages to /Home/Update nor display the JSON string on the view and just process the JSON in the back?
For your first question, check the following:
1) Make sure you have Microsoft.jQuery.Unobtrusive.Ajax included and referenced
2) OnSuccess = "MySuccessMethod()" should be OnSuccess = "MySuccessMethod" (where MySuccessMethod is a JavaScript method, not a C# one)
For your second question, you could have your method return ActionResult instead of JsonResult (see here for more information). JsonResult is a type of ActionResult, which means that updating your action to return ActionResult will allow your method to return multiple types of ActionResult depending on the scenario:
[HttpPost]
public ActionResult SomeThing(int randomParam)
{
if (randomParam == 0)
{
return Json("Zero!");
}
else if (randomParam == 1)
{
return View("Not zero!");
}
else
{
return HttpNotFound("Error: I can only find zeroes and ones");
}
}
As a general rule of thumb (although sometimes rules are meant to be broken), having your action return one type (like your example, JsonResult instead of ActionResult) makes your action less error-prone as, for example, Visual Studio will let you know if you accidentally try to return another type of result - use ActionResult when your action returns more than one type of result.

MVC Just populate viewbag once, between page default action result and httppost

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

A view that was not created gets displayed

I don't have a view called Test. But, there's a method in the Home controller called Test.
Everything works fine, the Test method gets executed and redirected to Index view as given in the code. However, in the browser the URL is ../Home/Test and not ../Home/Index. I don't have a View called Test so why is this getting displayed ? I don't want this URL to be displayed in the browser. How can i solve this ?
View:
#using (Html.BeginForm("Test", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />
..// Other code
}
C#
public ActionResult Test(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return View("../Home/Index", m); // Will be returning the Index View
}
In URL
"../Home/Test"
Test is action thus it is working fine.
If you don't want this URL to be displayed, renamed the Action "Test" to "Index" and also update its references
and use (optional)
return View("Index", m)
If you are using the standard setting for MVC, it is not the View, but The Method, that is displayed in the browser. The method then by default returns the View (but that is optional).
So what you need to do is rename you Test Method to Index and place [HttpPost] on top of it.
[HttpPost]
public ActionResult Index(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return View(m);
}
return View("../Home/Index", m);
This will not redirect you to Index; it will simply display your Index view. If you want browser to automatically change your URL from /Test to /Index you have to instead do this:
return RedirectToAction("Index");
Try this:
public ActionResult Test(HttpPostedFileBase f)
{
var m = new HomeModel();
..// Other code goes here
return RedirectToAction("Index");
}

Categories

Resources