How can I return a view(model) and also pass a query string param to the view? - asp.net-mvc

I have an MVC app where users fill in a 4-step form then go to a "confirm" screen. On the confirm screen, if they select to modify their info I use RedirectToAction to take them back to the first step view, and I pass a URL parameter "modify=true", which tells the controller to use the session object already created as opposed to creating a new object from the DB and displaying an empty form. But once they submit the form for step 1 I want to send them from my controller to the step 2 view along with the "modify=true" parameter. There doesn't seem to be a way to return a viewmodel to a view and also pass a query string parameter. How can I accomplish this?
I have considered adding a bool to the viewmodels to signify "inReview" but i use different viewmodels for each of these views and they're all pretty clean, it seems like this bool would muck things up a bit.
I have also considered adding the bool to viewbag or viewdata, but then i'd be using the submit button to pass that value and the "modify=true" parameter would drop off the URL, possibly confusing the user and definitely confusing the code.
Thanks

If you use the Html.BeginForm() helper (without parameters) it will automatically append existing query string parameters to the generated form action attribute. If you use some of the other overloads such as Html.BeginForm("SomeAction", "SomeController", FormMethod.Post) then you're gonna lose those parameters. This could be easily fixed by writing a custom helper that will take into account those parameters:
public static class HtmlHelpers
{
public static IDisposable BeginRequestForm(this HtmlHelper html, string action, string controller, FormMethod method)
{
var builder = new TagBuilder("form");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var routeValues = new RouteValueDictionary();
var query = html.ViewContext.HttpContext.Request.QueryString;
foreach (string key in query)
{
routeValues[key] = query[key];
}
builder.MergeAttribute("action", urlHelper.Action(action, controller, routeValues));
builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
html.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
return new MvcForm(html.ViewContext);
}
}
and then use in your view (after bringing it into scope of course):
#using (Html.BeginRequestForm("SomeAction", "SomeController", FormMethod.Post))
{
...
}

You can either use ViewBag or your view model. You just need to pass the value somehow to the view:
ViewBag.modify = true;
return View(model);
Then in your view:
Html.BeginForm("MyAction", "MyController", new { modify = ViewBag.modify })

Related

passing value in partial view viewdatadictionary

#Html.Partial("~/Areas/WO/Views/PartialContent/_FirstPage.cshtml", new ViewDataDictionary { { "WOID", WOID } })
In my Page i am accessing Partial view in the above way.
I need to pass WOID(view data dictionary) value from query string, For that i am using following Code
#{
var desc = Html.ViewContext.HttpContext.Request.QueryString.Get("ID");
Uri referrer = HttpContext.Current.Request.UrlReferrer;
string[] query = referrer.Query.Split('=');
int WOID = Convert.ToInt32(query[1]);
}
But the issue is this code is working in all browsers except I.E. i Need to Solve this problem.
Please help me
Instead of this you can have this value as part of you model and use that.That is the standard and recommeded way .
In your action method you can have these as parameter.Your query string value will get bind to this parameter
public ActionResult ActionMethod(int ID)
{
Model.WOID = WOID;
// Other logic
return View(Model)
}
Next step you can add this as a property to your view model or add it to ViewData dictionary and then access it in your partial view.

Html ActionLink not posting to action method

I am trying to submit to a controller action method by using Html.ActionLink in my view. I am doing this as non-ajax submit because the return type of my action is FileContentResult (thanks to #Darin for this info). However, my action link is not posting my view to Action Method. Below is my code
View's Code (Partial view)
#Html.ActionLink("Save", "SaveFile", "ui", new { htmlResult="asdf"})
Here, UI is controller name, SaveFile is method name.
Controller method
public FileContentResult SaveFile(string htmlString)
{
...
...
pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(html);
var cd = new ContentDisposition
{
FileName = "MyFile.pdf",
Inline = false
};
Response.AddHeader("Content-Disposition", cd.ToString());
return File(pdfBytes, "application/pdf");
}
When I hit the same URL from browser address bar, then it is hit and also returns the pdf file with no issues. Same this is not happening through action link. I also tried putting the action link inside #using Html.BeginForm().... but no use.
Can you please tell me where I might be doing wrong here?
Thanks!
Html.ActionLink has a lots of overloads and it's very easy to use the wrong one. You are currently using the (String, String, Object, Object) overload which treats your third argument "ui" this route values which leads to a wrongly generated link.
Use this overload instead:
#Html.ActionLink("Save", //Link text
"SaveFile", // Action Name
"ui", // Controller name
new { htmlResult="asdf"}, //Route values
null /* html attributes */)

Generate hyperlink in ASP.NET MVC 2 controller?

I've been scouring the web for a way to do this.
I want to generate a hyperlink to an action from my controller and put it in a string. I need to be able to define the label and give it html attributes. I can get Url.Action(...) working but that method doesn't let me define the label on the link.
HtmlHelper.GenerateLink(...) looks promising but I can't find any concrete examples on how to use it.
The link should look something like this:
View
Add this property to your base controller:
protected HtmlHelper Html
{
get
{
var viewContext = new ViewContext( ControllerContext, new WebFormView( Request.CurrentExecutionFilePath ),
new ViewDataDictionary(), new TempDataDictionary(), Response.Output )
{
RouteData = ControllerContext.RouteData
};
return new HtmlHelper( viewContext, new ViewPage() );
}
}
and then call it from anywhere:
var link = Html.ActionLink( "Click Me", "action" );
try this
string str = string.Concat("View"
and then pass this in ViewData and call it in view
<%= str%>
there are a few ways to do this - here are 2:
Link name here
Html.ActionLink(article.Title,
"Login", // <-- Controller Name.
"Item", // <-- ActionMethod
new { id = "<arguments here" }, // <-- Route arguments.
null // <-- htmlArguments .. which are none. You need this value
// otherwise you call the WRONG method ...
// (refer to comments, below).
)
there are other overloads of each available
Perhaps a little more information on why you would want to do this would be a little more helpful. If you return a string that contains HTML it will by default be HTML encoded and rendered useless on the client. If you have a custom view where this will be rendered why not build the link there using #Html.ActionLink?
I guess I am trying to figure out the benefit of doing it in the controller rather than the view...

How to modify posted form data within controller action before sending to view?

I want to render the same view after a successful action (rather than use RedirectToAction), but I need to modify the model data that is rendered to that view. The following is a contrived example that demonstrates two methods that that do not work:
[AcceptVerbs("POST")]
public ActionResult EditProduct(int id, [Bind(Include="UnitPrice, ProductName")]Product product) {
NORTHWNDEntities entities = new NORTHWNDEntities();
if (ModelState.IsValid) {
var dbProduct = entities.ProductSet.First(p => p.ProductID == id);
dbProduct.ProductName = product.ProductName;
dbProduct.UnitPrice = product.UnitPrice;
entities.SaveChanges();
}
/* Neither of these work */
product.ProductName = "This has no effect";
ViewData["ProductName"] = "This has no effect either";
return View(product);
}
Does anyone know what the correct method is for accomplishing this?
After researching this further, I have an explanation why the following code has no effect in the Action:
product.ProductName = "This has no effect";
ViewData["ProductName"] = "This has no effect either";
My View uses HTML Helpers:
<% Html.EditorFor(x => x.ProductName);
HTML Helpers uses the following order precedence when attempting lookup of the key:
ViewData.ModelState dictionary entry
Model property (if a strongly typed view. This property is a shortcut to View.ViewData.Model)
ViewData dictionary entry
For HTTP Post Actions, ModelState is always populated, so modifying the Model (product.ProductName) or ViewData directly (ViewData["ProductName"]) has no effect.
If you do need to modify ModelState directly, the syntax to do so is:
ModelState.SetModelValue("ProductName", new ValueProviderResult("Your new value", "", CultureInfo.InvariantCulture));
Or, to clear the ModelState value:
ModelState.SetModelValue("ProductName", null);
You can create an extension method to simplify the syntax:
public static class ModelStateDictionaryExtensions {
public static void SetModelValue(this ModelStateDictionary modelState, string key, object rawValue) {
modelState.SetModelValue(key, new ValueProviderResult(rawValue, String.Empty, CultureInfo.InvariantCulture));
}
}
Then you can simply write:
ModelState.SetModelValue("ProductName", "Your new value");
For more details, see Consumption of Data in MVC2 Views.
The values are stored in ModelState.
This should do what you want:
ModelState.SetModelValue("ProductName", "The new value");
I wouldn't suggest doing that though... the correct method would be to follow the PRG (Post/Redirect/Get) pattern.
HTHs,
Charles
EDIT: Updated to reflect the better was of setting the ModelState value as found by #Gary
This will trigger the model to re-evaluate under simple conditions:
ModelState.Clear();
model.Property = "new value";
TryValidateModel(model);
Perform ModelState.Clear() before you change the model.
...
ModelState.Clear()
dbProduct.ProductName = product.ProductName;
dbProduct.UnitPrice = product.UnitPrice;
...

How do you persist querystring values in asp.net mvc?

What is a good way to persist querystring values in asp.net mvc?
If I have a url:
/questions?page=2&sort=newest&items=50&showcomments=1&search=abcd
On paging links I want to keep those querystring values in all the links so they persist when the user clicks on the "next page" for example (in this case the page value would change, but the rest would stay the same)
I can think of 2 ways to do this:
Request.Querystring in the View and add the values to the links
Pass each querystring value from the Controller back into the View using ViewData
Is one better than the other? Are those the only options or is there a better way to do this?
i use a extension method for that:
public static string RouteLinkWithExtraValues(
this HtmlHelper htmlHelper,
string name,
object values)
{
var routeValues = new RouteValueDictionary(htmlHelper.ViewContext.RouteData.Values);
var extraValues = new RouteValueDictionary(values);
foreach (var val in extraValues)
{
if (!routeValues.ContainsKey(val.Key))
routeValues.Add(val.Key, val.Value);
else
routeValues[val.Key] = val.Value;
}
foreach (string key in htmlHelper.ViewContext.HttpContext.Request.Form)
{
routeValues[key] = htmlHelper.ViewContext.HttpContext.Request.Form[key];
}
foreach (string key in htmlHelper.ViewContext.HttpContext.Request.QueryString)
{
if (!routeValues.ContainsKey(key) && htmlHelper.ViewContext.HttpContext.Request.QueryString[key] != "")
routeValues[key] = htmlHelper.ViewContext.HttpContext.Request.QueryString[key];
}
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
return string.Format("{1}", urlHelper.RouteUrl(routeValues), name);
}
I would process the QueryString in the view (your option #1), instead of passing it in from the controller. This approach makes the view more self-contained, allowing you to convert it into a view control and re-use it across different views.
Note: Accessing the QueryString directly in the view may seem like a violation of the design principle of separating the Model and View, but in reality this data is a navigational concern which is related to the view, not really part of the model.
I would just keep the values in the Session that way the paging links only need to have;
/questions?page=2
/questions?page=3
The one reason why I would not us the QueryString is because I don't want the user to see the values that I am passing to the program. It makes it way too easy for them to go into the address bar and start changing the values to 'see what happens'. With this code all they could do is change the page number.
Here's how I done it in Asp.Net Core, first assign the query string parameters to ViewBags in your controller:
[HttpGet("/[controller]/[action]/{categoryId?}/{contractTypeId?}/{locationId?}")]
public IActionResult Index(Guid categoryId, int contractTypeId, Guid locationId)
{
ViewBag.CategoryId = categoryId;
ViewBag.ContractTypeId = contractTypeId;
ViewBag.LocationId = locationId;
...
}
Then pass the values to your links like so:
<a asp-action="Index" asp-controller="Jobs"
asp-route-categoryId="#teachingCategory.Id"
asp-route-contractTypeId="#ViewBag.ContractTypeId"
asp-route-locationId="#ViewBag.LocationId">
#teachingCategory.Description (#teachingCategory.Rank)
</a>
<a asp-action="Index" asp-controller="Jobs"
asp-route-categoryId="#ViewBag.CategoryId"
asp-route-contractTypeId="#typeOfEmployment.Id"
asp-route-locationId="#ViewBag.LocationId">
#typeOfEmployment.Name
</a>
<a asp-action="Index" asp-controller="Jobs"
asp-route-categoryId="#ViewBag.CategoryId"
asp-route-contractTypeId="#ViewBag.ContractTypeId"
asp-route-locationId="#item.Id">
#item.Id
</a>
Note that every link keep its own actual value and pass the rest of the route values through what we passed to ViewBag.

Resources