The Question
Is there a way to alias the parameters generated by MVC, in my case it would be the searchTerm param? For example, in my viewmodel it would still be SearchViewModel.searchTerm but the URL would look more like /Home/Search?s=jimmy
Search Form
<% using (Html.BeginForm("Search", "Home", FormMethod.Get)) {%>
<%: Html.TextBoxFor(model => model.searchTerm) %>
<input type="submit" value="Search" />
<% } %>
View Model
public class SearchViewModel
{
public string searchTerm { get; set; }
}
The URL Generated
/Home/Search?searchTerm=jimmy
Edit: Doesn't seem possible. Looks like I'll have to do it on my own..
I think you could either change your textbox control to not use templating and use the name your want to see in the query string like this...
<%= Html.TextBox("s", Model.searchTerm)
Or you could change the template that the TextBoxFor method uses with a own custom one, but I'm not sure what convention you would use. Maybe check for modelnames of "searchTerm"?
This site has some examples of how to create a custom template.
Sorry, if the syntax was not correct in the code, but I do not have MVC on this machine.
Related
I am trying to post a part of my model for a view to action in another controller. I've tried to do it this way:
<% using (Html.BeginForm("Register", "User", new {createDto = Model.UserCreateDto}, FormMethod.Post))
{%>
Form code here...
<%}%>
UserCreateDto is a part of my model and it's being filled in this form. I'm trying to pass it to "Register" action in UsersController:
[HttpPost]
public ActionResult Register(UserCreateDto createDto)
The problem is that route value in Register action is null (createDto) after form submit. Maybe it is happening because route values are being attached to url before form submit event (and before model fields are filled).
Is there any way to do what I want?
UPDATE Here is the actual HTML code:
Имя пользователя
Пароль
E-mail
And here is the updated form code:
<% using (Html.BeginForm("Register", "User", FormMethod.Post))
{%>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Username) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Password) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Email) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Active) %>
<%= Html.LabeledValidatedTextBoxFor(username => Model.UserCreateDto.Username, true) %>
<%= Html.LabeledValidatedTextBoxFor(password => Model.UserCreateDto.Password, true)%>
<%= Html.LabeledValidatedTextBoxFor(email => Model.UserCreateDto.Email, true)%>
<input type="submit" value="Register"/>
<%}%>
Html.LabeledValidatedTextBoxFor is just my custon HTML helper.
Here is updated action signature:
[HttpPost]
public ActionResult Register(UserCreateDto userCreateDto)
userСreateDto in this action is still null.http://www.media fire.com/file/5dcrv5zupy44c0i/A16.rar/file By the way, is there any way not to use hidden fields? They are not secure enough to pass through them user registration information.
Probably what you're looking for is ModelBinding. If your UserCreateDto Model is complex enough, and by that I mean that it contains any other complex type inside, the DefaultModelBinder is not going to know how to fill your model. So you need to tell it explicitly how to map the values in your request to the Model you are expecting in the Register post method in your UserController.
This link shows an example of how to do it in ASP.NET Core. I couldn't find the ASP.NET MVC official documentation but it's not so different than this.
Hope it helps!
I have an issue when using a hidden field in an MVC Form Post. When the hidden field is generated via a HTML Helper it won't preserve it's value during the postback. But when using a HTML tag, it works.
Unfortunately this one has taken me a whole day to work out this work around.
Here is what I'm doing... (excuse any spelling, re-typed code for SO):
View Model
public class SomeViewModel
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
}
Post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyActionMethod(SomeViewModel someViewModel, string command)
{
...
...
// someViewModel.MyProperty1
...
...
}
View
#using (Html.BeginForm("MyActionMethod", "SomeController", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.MyProperty1)
<div class="col-md-2">
<input type="hidden" value=#Model.MyProperty1 name="MyProperty1" />
<input type="submit" name="command" value="Button1" class="btn btn-primary" />
<input type="submit" name="command" value="Button2" class="btn btn-success" />
</div>
<div class="col-md-1"></div>
<div class="col-md-1">
<input type="submit" name="command" value="Button3" class="btn btn-danger" />
</div>
<div class="col-md-8"></div>
}
In the above View code, the HTML helper hidden field (#Html.HiddenFor(m => m.MyProperty1)) does NOT work. But the HTML tag hidden field (input type="hidden" value=#Model.MyProperty1 name="MyProperty1") DOES work. I only have one or the other enabled. Both shown here are for display purposes.
I'd prefer to use the HTML Helper syntax, but can live with the HTML tag.
Things to note:
The View is using multiple submit buttons.
The View is using a partial view. Currently no content in the partial view and nothing is being done with it.
I can't see how these would affect the issue. Thought I'd mention it, in case.
Question: Can anyone explain why the HTML Helper isn't working?
***** UPDATE *****
Thanks to Stephen Muecke for pointing out what needed to be included in my question. Moreover an extra thank you for guessing what I was actually doing but I couldn't articulate it.
I'm updating the View Model property in the ActionMethod(), and when the same View is re-rendered, the View Model property doesn't reflect the new value.
Rather it is keeping it's initial value, and not preserving the new value.
Although not obvious and I found it difficult to find many articles on this subject to clarify it for me in the past the default behaviour in ASP.NET MVC is the following:
If you are using HTML Helpers and you are rendering the same View in response to a POST it is assumed that you are responding to failed form validation.
Therefore the values before being set in the POST will always be rendered back in the view from ModelState.
You have a few options:
ModelState.Clear(); in your post. Not recommended as the framework has not been designed this way.
Use the Post-Redirect-Get pattern and only display validation failure, as the framework was designed (as #StephenMuecke mentions).
If you are not bothered about validation do not use HtmlHelpers
Use Request.Form values instead and remove the SomeViewModel someViewModel parameter. Wouldn't recommend this as you lose all benefits of model binding.
Use ModelState.Remove for the specific field, again not recommended.
The best article I found on this was article from Simon Ince in 2010:
ASP.NET MVC’s Html Helpers Render the Wrong Value!
Another one by Rick Strahl:
ASP.NET MVC Postbacks and HtmlHelper Controls ignoring Model Changes
I'm checking to see if anyone has written an MVC extension for Html.ActionLink that you can pass in Post parameters like such:
<% Html.ActionLink("Click me", "Index", "Home", new { MyRouteValue = "123" }, null, new { postParam1 = "a", postParam2 = "b" }); %>
That would render the link like normal but having an onClick event that submits an also rendered form with an Action url for the Action, Controller, and Route Values with additional hidden inputs from the Post Parameters like such:
Click me
<form id="theform" action="/Home/Index/123" method="post">
<input type="hidden" name="postParam1" value="a">
<input type="hidden" name="postParam2" value="b">
</form>
I'm looking to redirect users to various pages with potentially a lot of data. Not only from page to page, but from email to page also. This would be highly reusable and I think would clean up a lot of code, and would save a bunch of time writing this if its already floating around out there. I hate recreating the wheel when I don't have to.
ActionLink is just for creating an <a>. What you are asking for would blow up if it is already inside of a form. If it isn't then it is preferable to make the link the submit button inside the form and NOT use javascript (javascript and emails don't get along great).
You could create the form and appende it to the end of the DOM. This could be done through partial view or through javascript.
Honestly I suggest you don't use a POST. If you persist most of the data and just have the ids needed to retrieve said data, you should never have to pass too much data in an actionlink.
Ajax.ActionLink works perfectly fine for a post request. To refresh page, you can create a function that refreshes page (e.g. function reload(){ windows.location.reload();}).
It would look something like this.
#Ajax.ActionLink("DiaplyName", "Action", new { parameters to post }, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, OnComplete="reload();"})
Note: You'll need to reference the appropriate scripts to use ajax or jQuery code.
This piece of code was helpful for me and saved my day.. I improved it and it helped me for Impersonated user.. here is bellow ,what i did..
<% if (Session["SessionUserImpersonate"] != null && Session["SessionUserImpersonate"] != "" && Session["SessionUserImpersonate"] == "Yes")
{
BLL.Models.User userold = new BLL.Models.User();
userold = (BLL.Models.User)Session["SessionUserOld"];
%>
<span class="FL">(Impersonated as <%=Website.Backoffice.SessionHelper.Session_User.UserName != null ? Website.Backoffice.SessionHelper.Session_User.UserName:"" %> , </span>
<form class="FL" id='frmid' action="/Index/Login?username=<%=userold.UserName%>&password=<%=userold.Password%>&IsImpersonate=No" method="post">
<a class="TxtRed" style="cursor:pointer;" onclick="$('#frmid').submit(); return false;" > - finish impersonated session </a>
</form>
)
<%} %>
So, in example of DotNetOpenAuth I have form in aspx:
<form action="Authenticate?ReturnUrl=<%=HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"]) %>" method="post" id="openid_form" %>
</form>
And what will be analog of it aspx in Razor?
#using (Html.BeginForm(---???---)) {}
--- update---
Thanks for all for suggestions, answer is:
#using (Html.BeginForm("Authenticate", "Account", FormMethod.Post,
new { target = "_top", id = "openid_form" })){}
You don't need to call BeginForm; you can still write <form> tags in Razor.
BeginForm is used when posting to MVC routes.
If that is an MVC action, you can write
#using(Html.BeginForm("Authenticate", new { ReturnUrl = Request.QueryString["ReturnUrl"] }))
<form action="Authenticate?ReturnUrl=#Request.QueryString["ReturnUrl"]" method="post" id="openid_form" %>
</form>
There are multiple ways of doing it, though this is the cleanest way that most closely resembles the sample code you posted.
I am loving MVC 2. The whole thing just fits the web so well.
There is one piece of functionality, however, that I am unable to coax out of the Html.DisplayFor() function:
<# Page Inherits="ViewPage<IEnumerable<Foo>>">
<% foreach(var item in Model) { %>
<%: Html.DisplayFor(item.BarBaz) %>
<% } %>
I need to be able to use the DisplayTemplate for this value. Is there a way to do this?
Actually, I figured it out. How stupid of me.
This works:
<# Page Inherits="ViewPage<IEnumerable<Foo>>">
<% foreach(var item in Model) { %>
<%: Html.DisplayFor(m => item.BarBaz) %>
<% } %>
However, this will not work correctly for Html.HiddenFor and Html.ValueFor. In particular, Html.HiddenFor(m => item.NullableDecimal) will render as <input name="NullableDecimal" value="0" /> and Html.ValueFor(m => item.NullableDecimal, "0.00##) will render as 0.00##. However, if you apply a [DisplayFormat(DataFormatString = "{0:0.00########}" to your view model, then it will suddenly work. For this reason, you're probably best off using Html.Display, Html.Hidden, and Html.Value extensions, since you're less likely to run into scenarios where things fail when someone makes a non-local change.
You can accomplish it by getting away from the foreach and using a regular for loop:
<% for (int i = 0; i < Model.Count(); i++) { %>
<%: Html.DisplayFor(p=> p.ElementAt(i).BarBaz) %>
<%} %>
Another option would be to create a PartialView that took an Foo object and only displayed the information that you wanted.
<% foreach (var item in Model)
{
Html.RenderPartial("FooBarBazLine", item);
} %>
This is an old question, but i guess that someone can get benefits from my solution:
Aspx view
<%# Page Inherits="ViewPage<IEnumerable<Foo>>" %>
<% foreach (var item in Model) { %>
<%: Html.DisplayFor(m => item) %>
<% } %>
Razor view
#model IEnumerable<Foo>
#foreach (var item in Model)
{
Html.DisplayFor(m => item)
}
Since DisplayFor accepts an implicit typed lambda, we can directly indicate the instance to display in the loop.
Finally, i'm agree with Anders E. Andersen's answer for the template usage
Html.DisplayFor can automatically iterate over collections, displaying a partial view for each element in the collection.
The first thing you need to do is create an actual model class, with the collection being a property of the class.
public class Bar
{
public IEnumerable<Foo> foo { get; set; }
}
Return this class from your controller instead of the raw collection.
Secondly you need a display template for the Foo class. Display templates are partial views that need to be placed in the folder Views/Shared/DisplayTemplates.
Edit: You can have them in your controller subfolder of Views as well if you want to limit the template to a particular controller. See this question for more information.
Here is an example in razor syntax:
#model YourNameSpace.Foo
<p>#Model.BarBaz</p>
Save it as Foo.cshtml in the DisplayTemplates folder given above.
This template is pretty simple because it is based on your example where you are only displaying a string, but if the collection elements where a class with its own properties you could create a more elaborate template.
Now in the original view, you can get rid of the loop entirely and just write
#Html.DisplayFor(m => m.foo)
Notice foo is the name of the property in your new model class that contains the old collection you looped over before.
DisplayFor will automatically know that the foo property is of type (collection of) Foo and pick up the Foo.cshtml template in the DisplayTemplates folder and show it once for each element in foo.
In Razor I am using
#Html.DisplayFor(model => item.CreatedDate)
Just be careful that a DisplayFor on a POST of a form doesnt actually create a field that can be referenced back in the controller. You would need to use an additional HiddenFor if you need to refer to fields in your submitted POST method.