I have two text inputs, content of which needs to be passed to a certain action as parameters when a button is clicked. I'm using MVC3
View:
<input name="input2" type="text" class="inputfield" id="datepicker_1" /></td>
<input name="input2" type="text" class="inputfield" id="datepicker_2" /></td>
#Html.Action("Search", ...)
Controller:
public ActionResult Search(...)
I suppose the object routeValues or RouteValueDictionary should be used in the #Html.Action for this. These object are confusing for me a bit. Could anyone clarify this for me please. Thank you!
The Html.Action will probably generate the link html before you provide the inputs. You need to either place your inputs inside a form to be submited to your action, or use ajax, with jquery perhaps, to call the action, like so:
#using (Html.BeginForm("Search", "Controller", FormMethod.Post, new { id = "frmAction" }))
{
<input name="datepicker_1" type="text" class="inputfield" id="datepicker_1" /></td>
<input name="datepicker_2" type="text" class="inputfield" id="datepicker_2" /></td>
}
[HttpPost]
public ActionResult Search(Datetime datepicker_1, Datetime datepicker_2) {...}
For an Ajax example, check this question:
jquery ajax forms for ASP.NET MVC 3
Hope this helps...
It seems like you need have a submit on your view, and then implement a post action on your controller, to handle the submit. I.e. something along the lines of
<input type="submit" value="Search..." />
in the view, and
[HttpPost]
public ActionResult Search(FormCollection collection)
the parameters depend on whether your view is strongly typed. The above assumes its not.
Related
Following code in razor view is not creating HTML with input type hidden with name "__RequestVerificationToken" strange. I have "LogOff" action decorated with attribute "ValidateAntiForgeryToken".
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post))
{
Html.AntiForgeryToken();
<input type="submit" value="LogOff" />
}
What does I am missing here?
It should be
#Html.AntiForgeryToken()
not
Html.AntiForgeryToken();
Try using
#Html.AntiforgeryToken()
#Html.ValidationSummary() //if needed
and also check if there is the
[HttpPost]
[ValidateAntiForgeryToken] attributes placed on your action method.
Hope this helps.
Thanks
I'm trying to post a model back to my controller. I'm not sure why TextBoxFor works and TextBox does not.
#ModelType Models.myModel
#Code
ViewData("Title") = "Foo"
End Code
#Using (Html.BeginForm())
#Html.AntiForgeryToken()
#Html.TextBoxFor(Function(m) m.Value) <--- works
#Html.TextBox(Model.Value) <--- does not work
#Html.TextBox("Value", Model.Value) <--- works! (per answer below)
#<input type="submit" value="Save" />
End Using
Html.TextBoxFor creates right input type="text" with correct name value. You mustn't use it, but it helps in this case. Same result may be achieved even with html code:
<input type="text" name="Value" value="#Model.Value" />
Main point for asp.net mvc model binders is correct names for html controls. Html.TextBox() also does it, using first parameter as html element name, so correct usage of it should be Html.TextBox("Value", Model.Value)
I use an ajax form for removing items from a list. The first time I submit something, it works but the second times, the reference of the item submitted is not correct: it is the first reference that is still used.
Here is my ajax form:
<div>
<table>
#foreach (var item in Model.ProjectTechnology)
{
<tr>
<td>#Html.DisplayFor(m => item.TechnologyID) </td>
<td>#using (Ajax.BeginForm("RemoveLinkedTechnology", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "AddedTechnologies" })) {
#Html.Hidden("projectID", item.ProjectID)
#Html.Hidden("removedTechnologyID", item.TechnologyID)
<input type="submit" value="Suppr" />
}</td>
</tr>
}
</table>
</div>
Here is the action in my controller:
[HttpPost]
public ActionResult RemoveLinkedTechnology(int projectID, string removedTechnologyID)
{
// some code here...
}
Example:
Lets say I proceed the submitting like this: first submit: AA; second submit: BB.
For the first call: removedTechnologyID contains AA.
For the second call: removedTechnologyID still contains AA.
Any idea?
Thanks
I suspect that in your controller action you are returning a partial view which updates the contents of the <table> you have shown. Now since Html helpers such as Hidden or TextBox first look for values in ModelState before binding and then in the model what happens is that #Html.Hidden("removedTechnologyID", item.TechnologyID) sees that there is a removedTechnologyID="AA" in the model state and completely ignores your model value which is item.TechnologyID. So if you have looked at the DOM after the first AJAX request you would have seen that all hidden fields have the old values inside them.
To fix this you have 3 possibilities:
Clear the item in model state in your controller action:
[HttpPost]
public ActionResult RemoveLinkedTechnology(int projectID, string removedTechnologyID)
{
...
ModelState.Remove("removedTechnologyID");
ModelState.Remove("projectID");
return View(...);
}
Don't use helpers to generate the hidden fields:
<input type="hidden" name="projectID" value="#item.ProjectID" />
<input type="hidden" name="removedTechnologyID" value="#item.TechnologyID" />
Write a custom Html.Hidden helper which will first use the values in the model before looking at modelstate (out of scope for this answer)
I have some problems with ASP.NET MVC’s default model binder. The View contains HTML like this:
<input name="SubDTO[0].Id" value="1" type="checkbox">
<input name="SubDTO[1].Id" value="2" type="checkbox">
This is my simplified ‘model’:
public class SubDTO
{
public virtual string Id { get; set; }
}
public class DTO
{
public List<SubDTO> SubDTOs { get; set; }
public DTO()
{
SubDTOs = new List< SubDTO>();
}
}
All this works fine if the user selects at least the first checkbox (SubDTO[0].Id). The controller ‘receives’ a nicely initialised/bound DTO. However, if the first check box is not selected but only, for example, SubDTO[1].Id the object SubDTOs is null. Can someone please explain this ‘strange’ behaviour and how to overcome it? Thanks.
Best wishes,
Christian
PS:
The controller looks like this:
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public RedirectToRouteResult Create(DTO DTO)
{
...
}
PPS:
My problem is that if I select checkbox SubDTO[0].Id, SubDTO[1].Id, SubDTO[2].Id SubDTOs is initialised. But if I just select checkbox SubDTO[1].Id, SubDTO[2].Id (NOT the first one!!!) SubDTOs remains null. I inspected the posted values (using firebug) and they are posted!!! This must be a bug in the default model binder or might be missing something.
This behavior is "by design" in html. If a check-box is checked its value is sent to the server, if it is not checked nothing is sent. That's why you get null in your action and you'll not find value in the posted form either. The way to workaround this is to add a hidden field with the same name and some value AFTER the check-box like this:
<input name="SubDTO[0].Id" value="true" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="true" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
In this way if you check the check-box both values will be sent but the model binder will take only the first. If the check-box is not checked only the hidden field value will be sent and you\ll get it in the action instead of null.
I think this post on Scott Hanselman's blog will explain why. The relevant line is:
The index must be zero-based and unbroken. In the above example, because there was no people[2], we stop after Abraham Lincoln and don’t continue to Thomas Jefferson.
So, in your case because the first element is not returned (as explained by others as the default behaviour for checkboxes) the entire collection is not being initialized.
Change the markup as follows:
<input name="SubDTOs" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTOs" value="<%= SubDTO[1].Id %>" type="checkbox">
What's being returned by your original markup is an unrelated set of parameters, i.e. like calling RedirectToRouteResult Create(SubDTO[0].id, SubDTO[1].id, ..., SubDTO[n].id) which is clearly not what you want, you want an array returned into your DTO object so by giving all the checkboxes the same name the return value to your function will be an array of ids.
EDIT
Try this:
<input name="SubDTO[0].Id" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="<%= SubDTO[1].Id %>" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
You have to return something to make sure there is an element for each index, I suspect that any gap will cause a problem so I'd suggest using a 'null' ID, for example 0 or -1 and then process that out later in your code. Another answer would be a custom model binder.
There is always the alternate option of adding a property to your class that takes an array of strings and creates the SubDTO array from that.
public List<string> SubDTOIds
{
get { return SubDTO.Select(s=>s.Id).ToList(); }
set
{
SubDTOs = new List< SubDTO>();
foreach (string id in value)
{
SubDTOs.Add(new SubDTO { Id = id });
}
}
}
or something like that
I have an HTML table where each row has buttons which toggle status bits in the database for each row. Assuming Javascript is not an option, what would be the "best practice" way of handling this?
I'm currently handling it by wrapping each row in a form like this:
<table>
<tr>
<td>
<form action="/FooArea/BarController/BazAction" id="frm0" name="frm0" method="post">
<span>Item 1</span>
<input type="submit" value="Toggle1" name="submitButton" />
<input type="submit" value="Toggle2" name="submitButton" />
<input type="hidden" name="itemID" value="1" />
</form>
</td>
</tr>
<tr>
<td>
<form action="/FooArea/BarController/BazAction" id="frm1" name="frm1" method="post">
<span>Item 2</span>
<input type="submit" value="Toggle1" name="submitButton" />
<input type="submit" value="Toggle2" name="submitButton" />
<input type="hidden" name="itemID" value="2" />
</form>
</td>
</tr>
</table>
And the post method looks something like this:
string buttonName = Request.Form["submitButton"];
if (!String.IsNullOrEmpty(buttonName ))
{
int itemID = Convert.ToInt32(Request.Form["itemID"]);
switch (buttonName )
{
case "Toggle1":
DoSomething(itemID);
break;
case "Toggle2":
DoSomethingElse(itemID);
break;
}
}
Any better suggestions? Is having 50 forms on a page cool? I dunno.. let me know what you are doing in this case.
The best way to handle POST scenarios without JavaScript is, as several others have stated, a tiny form with only the necessary values, and only one submit button. Basically, what you should do is to create a helper method that creates a form with the necessary POST values in hidden fields and a submit button. For example, you could have a method you use like this:
<%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", new List<KeyValuePair<string, string>>{ new KeyValuePair<string, string>("itemId", 1), new KeyValuePair("action", "option1") }); %>
It looks pretty verbose, but I've tried to make it as generic as possible. You can probably create the List<KeyValuePair<string, string>> in the controller when you render the view, so you only have to call something
<%= Html.PostLink("FooArea/BarController/BazAction", "Toggle1", Model.Values) %>
In the action method that handles the post, you bind to the posted FormCollection, and retrieve the values of itemId and action to determine what to do, instead of checking for Request.Form values.
An implementation of the helper method might look like this:
public static string PostLink(this HtmlHelper helper, string postAction, string submitText, IEnumerable<KeyValuePair<string, string>> postValues)
{
var form = new TagBuilder("form");
// Setup basic properties like method, action
form.Attributes.Add("method", "post");
form.Attributes.Add("action", postAction);
// Instantiate a stringbuilder for the inner html of the form
var innerHtml = new StringBuilder();
// Create and append hidden fields for the post values
foreach(var value in postValues)
{
var hidden = new TagBuilder("input");
hidden.Attributes.Add("type", "hidden");
hidden.Attributes.Add("name", value.Key);
hidden.Attributes.Add("value", value.Value);
innerHtml.Append(hidden.ToString(TagRenderMode.SelfClosing));
}
// Create the submit button
var submit = new TagBuilder("input");
submit.Attributes.Add("type", "submit");
submit.Attributes.Add("value", submitText);
// Append it to the stringbuilder
innerHtml.Append(submit.ToString(TagRenderMode.SelfClosing));
// Set the InnerHtml property of the form, and return it
form.InnerHtml = innerHtml.ToString();
return form.ToString(TagRenderMode.Normal);
}
This seems like a prime case for some JQuery and Ajax functionality. But if javascript isn't an option then i think wrapping each one in a form is probably the most simple solution.
When I need to do something like this I do it pretty much the same way as you do here. It is no problem to have multiple forms in one view. Its like having multiple links in one view (but they use POST instead of GET). I guess many .net developers think its wrong because of webforms only having one form tag. In your case I would even create more forms for each submit button as they seem to do different things. I consider having to check the name of the button in the controller to be a code smell. If they do something like activate/deactivate you should probably post that as a bool or something and have both forms post to the same action.
I think technically this is the best way to go. From a usability point of view you can question if this is the best way to do it. More than 20 items in a list can get quite confusing. But ofcourse, I don't really know what you are programming :)
I agree with Mattias about creating different actions for the activate and deactivate-button. This way you avoid having to use a switch-statement.