MVC Hiding RouteValues Form Post from Url's - c#

Working with Multiple Steps on a form and different views sharing one model.
On my second Step Form Post submit I am passing in my model to maintain the state.
<% using (Html.BeginForm("StepTwo", "Home", Model, FormMethod.Post, new { id = "restrictionForm" })) { %>
But I find when I hit submit and it works the way I want, my entire model and its attributes are now listed in the url which is not desirable.
[Authorize]
[HttpPost]
[ActionName("StepTwo")]
[ValidateAntiForgeryToken]
public ActionResult StepTwoPost(PostcodesModel model)
{
try
{
_Provider.AddNewRestriction(model.Postcode, model.SelectedRestriction, model.RestrictionDescription, model.WildcardID);
return View("StepThree", model);
}
catch(Exception ex)
{
ViewBag.PostCodeErrors = "<div class=\"errorMessage\">Error inserting restriction " + model.Postcode + ".</div><p>" + ex.Message + "</p>";
return View(model);
}
}
Anyway to now display model in URL this way ?
i.e.
http://localhost:/Home/StepThree?Postcode=1234&.....

can you post the complete view?,
how are are you rendering your Models values? if you are rendering them as routing data within the submit button these will cause them to appear on the URL, you should render them the model within HTML elements "Textbox" or "Hidden" for example and these values will be posted as form data

Related

jQuery post to controller and redirect to ASP.NET MVC view

I have some checkboxes and a button (not in a form).
When the button is clicked, I have some jQuery and I am creating a post model which contains the values of the checked boxes and posting to a controller.
The controller then creates view models and I want to redirect the user to the correct view, passing the view model in to the view.
jQuery:
$.ajax({
url: AppSettings.baseUrl + "BOM/getMultiBOM",
type: 'POST',
data: JSON.stringify(data)
});
Controller:
[HttpPost]
public ActionResult getMultiBOM(multiBOMPostModel multiBomsPostModel)
{
BOM bom = null;
foreach (int value in multiBomsPostModel.bomsArray)
{
bom = db.BOMs.Find(value);
}
BOMViewModel viewModel = getViewModel(bom, null);
return RedirectToAction("OpenMultiBOM", new { viewModel = viewModel, bom = bom });
}
public ActionResult OpenMultiBOM(BOMViewModel viewModel, BOM bom)
{
viewModel.projectModel = new ProjectViewModel
{
project = bom.Project
};
return View(viewModel);
}
It is probably a bit of a mess.
I think the jQuery is necessary to pass the checkbox values to the controller.
When I use RedirectToAction to the method which then returns the view, the model is not being passed through, presumably as it is sending the model as a query string.
The view model is not simple and contains lists, IEnumerables, and nested models.
Can anyone help with the most efficient way to redirect/return the view while passing the view model?
Answer: I kept the ajax to post my checkbox values to the controller
$.ajax({
url: AppSettings.baseUrl + "BOM/getMultiBOM",
type: 'POST',
data: JSON.stringify(dataArr),
}).done(function (result) {
location.href = "/BOM/OpenMultiBOM";
});
In my controller, I assigned the posted values to a postModel and then stored them in TempData. The key here was to return a Json value which would then allow the redirect on the client side to take place.
public ActionResult getMultiBOM(multiBOMPostModel multiBOMPostModel)
{
TempData["BOMs"] = multiBOMPostModel;
return Json("success");
}
I then had another HttpGet method which would load after the page is redirected by the Ajax result and cast the TempData to an object.
[HttpGet]
public ActionResult OpenMultiBOM(int? BomMarkupMessage = null)
{
var bomIds = TempData["BOMs"] as multiBOMPostModel;
}
I would persist the viewmodel server side, perhaps in a session variable, or perhaps as a TempData (TempData typically only lives until the next request), and pass a key for the session variable to the second controller in the case of session variable, or use the TempData directly in your view in the case of TempData. This would avoid passing the whole object back and forth multiple times.
So the way that i have done this before is to have an empty div in DOM.
<div id="partialViewContent">
<!-- Content will be loaded later. -->
</div>
If you have a default view, you'll need to set it to load from URI using the below snippet.
$("#partialViewContent").load("Controller/Action",
function (response, status) {
if (status !== "success") {
console.log("An error has occured when attempting to load partial view.");
}
});
When you post to your controller action via JQUERY, have the action return a partial view with the model. (Assume model is relevant to each partial view).
Then when your ajax is complete, replace the content in partialViewContent with the POST response.

ASP.net MVC 5 loading HTML.Partial from controller instead of loading on cshtml page

new to MVC so here goes.
I am currently loading up HTML.Partial on my Index.cshtml page like so:
#Html.Partial("~/Views/Switchb/editPerson.cshtml")
However, I am in need of customizing that in the controller depending on the current users category number.
So as an example, if the user has a category of 3 then I would need to do this:
#Html.Partial("~/Views/Switchb/3.cshtml")
Is there any type of call in the "code behind" in the controller that I can use in order to do that? Or would I just need to place the code within the cshtml page and pass its category number via the controller over to the cshtml page?
You can render a partial view from a controller action. You can pass the view name as string.
public ActionResult Switchb(string categoryNumber) {
var viewModel = new MyViewModel { CategoryNubmer = categoryNumber };
// additional processing, backend calls, formatting ....
return PartialView(categoryNumber, viewModel);
}
To call this action from View:
#{
var routeValues = new RouteValueDictionary(new {
categoryNumber= "3",
});
Html.RenderAction("Switchb", "MyController", routeValues);
}
Determine the category in the controller (via url parameter, from a database, or whatever) and then set that value as a property on your view's Model. Then in your line of code you can do this
#Html.Partial("~/Views/Switchb/" + Model.Category + ".cshtml");

Redirect within partial view in MVC

I have kind of a weird scenario. I'm creating a site using the ASP.NET MVC Framework that contains a profile page which has a user's picture, info, etc. I have a view called Profile which loads partial views using the Ajax action link into a div. Here is an example:
#Ajax.ActionLink("Basic Info", "Index", "BasicInfo",
new {id=Model.UserName},new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "content",
HttpMethod = "GET"
})
The Index action of BasicInfo just shows the user's basic information. I want to have an edit link in that partial view that when pressed loads another action method, Edit where I can edit the values (another partial view). I have a couple of questions:
1) I didn't make Profile a layout even though it would be similar to a master page in ASP.NET because it would need a controller. Is there a way to create a layout that has its own controller?
2) How do I do a redirect within a partial view without doing a postback i.e. updating the div holding the partial view that was previously called by Ajax to an edit view?
3) I hope this all makes sense. I would see a profile with the person's basic information and I can press edit within that view and the edit view of that basic information controller is loaded into the div without doing a postback. What would be the best way to accomplish this?
You will find this a it easier using the jQuery ajax methods rather than the Ajax.ActionLink() and Ajax.BeginForm() methods. In the main view
<button type="button" class="details" data-id="Model.UserName">View details</button>
<div id="content"></div>
var detailsUrl = '#Url.Action("Details", "User")';
var editUrl = '#Url.Action("Edit", "User")';
// Display the details view
$('.details').click(function() {
$.get(detailsUrl, { id: $(this.data('id') }, function(response) {
$('#content').html(response);
});
});
// Display the edit view
$('#content').on('click', '#edit', function() {
$.get(editUrl, { id: $(this.data('id') }, function(response) {
$('#content').html(response);
});
});
// Post the edit form and replace with the updated details view
$('#content').on('click', '#save', function() {
var id = $(this).data('id');
var data = $(this).closest('form').serialize();
$.post(editUrl, data, function(response) {
if (response) {
$.get(detailsUrl, { id: id }, function() {
$('#content').html(response);
});
} else {
// Oops
}
}).fail(function (result) {
// Oops
});
});
The above assumes a UserController with the following methods
public PartialViewResult Details(int ID) // or string?
{
// Get the user model based on the ID
return PartialView("_Details", model);
}
public PartialViewResult Edit(int ID) // or string?
{
// Get the user model based on the ID
return PartialView("_Edit", model);
}
public JsonResult Edit(UserModel model) // replace with the name of your model
{
// Save the model
return Json(true); // indicate success
}
where the partial views are
_Details.cshtml
#model UserModel
.... // display properties of the model
<button type="button" id="edit" data-id="Model.UserName">Edit</button>
_Edit.cshtml
#model UserModel
<form>
.... // controls for properties of the model
<button type="button" id="save" data-id="Model.UserName">Save</button>
</form>
I might be misunderstanding things.
I think you're trying to flip a display view of a part of a page for an edit view of that part of the page.
I'll keep things general, because there's not enough code to refer to directly.
You should register javascript event handlers against the various clicks that can take place (in a jquery closure in a separate file is my personal preference). Those handlers should request whichever actions (returning partial views) are required.
e.g. when someone clicks the edit link, the handler calls the /GetEditStuff action, gets the partial view, and on success, clears out the previous content of the parent div and replaces it with the partial view.

MVC4 - Form data and refreshing on a dynamically rendered view

Been working on creating an interface to allow a modular approach to the UI, the background:
Allows users to drag and drop a module onto a div
jQUery posts back to controller with the module and panel names
Controller returns a JsonResult containing a view that has been rendered, specific to that module
Here is a picture of the UI so you can sort of see what I am doing:
Image
Now, what I am trying to do, is in that JsonResult (Which contains a string output of a view rendering), is save some data back to the model, and refresh that dynamically rendered view, so that just the panel (Where the view has been rendered) updates.
Sounds complicated i know, so here is some code:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult AddModule(string id, string returnTo)
{
string content = RenderView(id);
return Json(new { Target = returnTo, Content = content });
}
private string RenderView(string moduleName)
{
string result = "";
ContentModule module = (ContentModule)Activator.CreateInstance(Type.GetType("TrustMRM.BLL.ContentModules." + moduleName + ",TrustMRM.BLL"));
module.TrustID = Settings.Default.TrustID;
module.DataBind();
this.ViewData.Model = module;
using (var sw = new System.IO.StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(this.ControllerContext, moduleName);
var viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw);
viewResult.View.Render(viewContext, sw);
result = sw.GetStringBuilder().ToString();
}
return result;
}
The above is what handles the 'drop' of the module. I have an abstract class, ContentModule, and an implementation called BLLForumModule, there is a matching view, BLLForumModule.cshtml, that gets built, and returned in that string, strongly bound tot he BLLForumModule.
What is rendered is a drop down list, equal to some data to configure that particular module:
#model TrustMRM.BLL.ContentModules.BLLForumModule
#{
Layout = null;
}
#if (Model.IsConfigured)
{
<span>I am configured</span>
}
else
{
using (Html.BeginForm("RefreshModule", "Home"))
{
<h3 class="panelHeader">#Html.DisplayTextFor(m => m.Title)</h3>
<span>Select group</span>
#Html.DropDownListFor(m => m.SelectedGroupID, Model.GroupSelection.Select(t => new SelectListItem { Text = t.GroupName, Value = t.GroupID.Value.ToString() }));
#Html.HiddenFor(x => x.ModuleID);
<input type="submit" value="Ok" />
}
}
Now, I am unsure of what to return, or how to handle this post in order to refresh that view, the one that was rendered as a string and sent back, any insight into this, and if anyone has done something similar before, perhaps my rendering the view to a string is the wrong approach?
The code to accept the form post:
public ActionResult RefreshModule(string ModuleID)
{
return View();
}
(Doesn't work)
Something like that will help you
Using Ajax.BeginForm with ASP.NET MVC 3 Razor
Just use Ajax.BeginForm and provide an id of replaced element.
Attach validation after ajax request here
MVC3 Unobtrusive Validation Not Working after Ajax Call

ValidationSummary not appearing with Partial Views

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.

Categories

Resources