Edit and Create view using EditCreate.ascx partial in ASP.NET MVC - asp.net-mvc

If you look at the NerdDinner example of creating and editing dinners then you see they use a partial (ViewUserControl or ASCX) DinnerForm to put the functionality of creating and editing dinners into one file because it is essential the same and they use it using RenderPartial("DinnerForm").
This approach seems fine for me but I've run into a problem where you have to add additonal route values or html properties to the Form tag.
This picks up the current action and controller automatically:
<% using (Html.BeginForm()) { %>
However, if I use another BeginForm() overload which allows to pass in enctype or any other attribute I have to do it like this:
<% using ("Create", "Section", new { modal = true }, FormMethod.Post, new { enctype = "multipart/form-data" }))
and as you can see we lose the ability to automatically detect in which View we are calling RenderPartial("OurCreateEditFormPartial"). We can't have hardcoded values in there because in Edit View this postback will fail or won't postback to the right controller action.
What should I do in this case?

What about adding the following HTML helpers:
public static MvcHtmlString CurrentAction(this HtmlHelper htmlHelper)
{
return htmlHelper.ViewContext.RouteData.Values["action"];
}
public static MvcHtmlString CurrentController(this HtmlHelper htmlHelper)
{
return htmlHelper.ViewContext.RouteData.Values["controller"];
}
Then you could go something like this:
<% using (Html.CurrentAction, Html.CurrentController, new { modal = true }, FormMethod.Post, new { enctype = "multipart/form-data" }))
Its not 100% perfect but you could also add an additional HTML helper which would streamline it a little more:
public static MvcForm BeginForm(this HtmlHelper htmlHelper, object routeValues, FormMethod method, object htmlAttributes)
{
return htmlHelper.BeginForm(Html.CurrentAction, Html.CurrentController, routeValues, method, htmlAttributes);
}
Let me know if this helps.
Cheers

I'm answering in an old thread because it came up number two on a Google search whilst I was looking for the same thing :) Found on this SO post:
Html.BeginForm and adding properties
With MVC3 (not sure about MVC2) you can pass null in for the action and controller and get the default routes that BeginForm() would use.
#Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data"})
Cheers!

Related

MVC: Html.ActionLink() Resolves to (Edit?Length=) instead of (Edit?Id=)

I have created an Admin area in my MVC 4 application. Under Views Folder called UserManage, I am using Grid.MVC to list out users (sets of 5) over several pages. In the Grid I am listing out the users details for viewing by Admin, and have added:
A column containing a checkbox [ ].
A column containing an EDIT link.
A CREATE button.
A DELETE button (to be used in conjunction with the checkbox for each row).
My User Details page is under my Admin Area in the following path: Project\Areas\Admin\Views\UserManage\Index.cshtml. On my EDIT liniks, I am attempting to pull up the view at Project\Areas\Admin\Views\UserManage\Edit.cshtml and pass the individual UserID to the View to pull up that users data.
In my current code below, my browser resolves to http://localhost:62517/Admin/UserManage/Edit?Length=4 with an IIS 8.0 404 Error (Controller cannot find UserID). However if I manually change the Length to Id as follows, everything works as intended and I end up on my Edit view: http://localhost:62517/Admin/UserManage/Edit?Id=4.
Here is my grid code for the Index.cshtml page. Does anyone know what I need to modify on my Html.ActionLink() so my route will resolved to ?Id= instead of ?Length=?:
<div class="overflowPrevention">
#Html.Grid(Model).Columns(columns =>
{
columns.Add().Encoded(false).Sanitized(false).SetWidth(30).RenderValueAs(o => #Html.CheckBox("checked", false));
columns.Add().Encoded(false).Sanitized(false).RenderValueAs(o => Html.ActionLink("Edit", "Edit", "Edit", new { id = "1" }));
}).AutoGenerateColumns()
</div>
You are using the wrong overload.
#Html.ActionLink(string linkText, string actionName, string controllerName, object htmlAttributes)
instead of
#Html.ActionLink(string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
You can just pass null for htmlAttributes if you don't need to use them:
Html.ActionLink("Edit", "Edit", "UserManage", new { id = "1" }, null)
I'm not sure why you are using Edit, Edit, Edit though. I think that was a typo so I have changed it to use UserManage as the controller name.

Using Html.BeginForm with querystring

My url looks like this:
customer/login?ReturnUrl=home
In the login view, I have used this pattern of code which works fine.
#using(Html.BeginForm())
{
...
}
This magically generates following html
<form action="customer/login?ReturnUrl=home" method="post">
But now, I need to add an attribute (e.g., data-id="something") in the form. How can I do that? If I don't have any query string, I know I can do something like this:
#using(Html.BeginForm(action, controller, FormMethod.Post,
new { data_id="something" }))
But don't know how to add querystring which should be in html:
<form action="customer/login?ReturnUrl=home" method="post" data-id="something">
I thought about using <form> directly but don't know how to specify querystring which is variable. And I have no idea how to achieve it with Html.BeginForm. Any tip would be appreciated.
RESOLUTION:
For now, I used <form> with following hint How to get current url value in View. The resulting view looks like
<form action="#Request.Url.PathAndQuery" data-id="something" method="POST">
But it would be nice to have an overloaded method of BeginForm for this.
Here's The way that worked for me
Html.BeginForm("Profile", "Partner", routeValues: new {id=Partner.partner_id},method:FormMethod.Post)
It was almost like there was a problem with overloading the method, but by specifying what things are, it seems to work fine...
To create a RouteValueDictionary from the querystring:
RouteValueDictionary queryStringDictionary = new RouteValueDictionary(Request.QueryString.AllKeys.ToDictionary(key => key, key => (object)Request.QueryString[key]));
Then you can use it with Html.BeginForm:
Html.BeginForm(null, null, queryStringDictionary, FormMethod.Post, new Dictionary<string, object> { { "autocomplete", "off" } })
I guess this doesn't directly answer the question, but why not just use a plain old form tag?
<form action='customer/login?ReturnUrl=#Request.QueryString["ReturnUrl"]' method="post" data-id="something">
Alternatively, you can create a custom HtmlHelperExtension that renders a form with path and querystring. In this HtmlHelperExtension you can iterate through your querystring values and populate the routeValueDictionary which you then pass to a Html.BeginForm constructor.
If you don't want something so extensible you can just use the overloaded constructor of Html.BeginForm using
#Html.BeginForm("login", "customer", new {ReturnUrl = #Request.QueryString["ReturnUrl"]},FormMethod.Post, new {data-id="something"});
using Reflector to look at the code,
BeginForm() will pass directly the rawUrl over to the final Form.
Any other overloads on BeginForm will go through a helper utility which will strip the query string.
This works for me :
#using (Html.BeginForm("index", "Photos", routeValues: new { user = pUser, album = pAlbum, }, method: FormMethod.Get))
Explicit route values and method is what is required...
Just incase you wanted to add other attributes as well. use below code
#using (Html.BeginForm("actionName", "controllerName", routeValues: new { lang = "en" }, method:FormMethod.Post, htmlAttributes: new { #class= "my-form", enctype = "multipart/form-data" }))
Try #using(Html.BeginForm(null, null, FormMethod.Post, new { data_id="something" }))
It should use the default logic to construct the url, just as if you used BeginForm()
(never tried that though in such case, but I believe it should work)

ASP.Net MVC - Helper lambdaexpression post action

I created this helper method using a lambdaexpression to used strongly type helper in a view
Helper
public static string DateFor<TModel, TDate>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TDate>> expression)
{
ModelMetadata data = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
StringBuilder sb = new StringBuilder();
...
//code that creates three dropdownlist (day, month and year)
...
}
view
<%= Html.LabelFor(model => model.DataNascita) %>
Controller
[HttpPost]
public ActionResult Edit(int id, Account MyAccount)
{
...
return View(...);
}
my problem is that the MyAccount.DataNascita is not set with the value i choose in the Edit form (date's value minimun.. ex. 1900/01/01).
how to bind it in a Edit post action?
I think what you are after is a custom ModelBinder that will parse the incoming posted data and convert it into a DateTime for your model.
In your view, you say that you are outputting a Label for something, but your question concerns why the value isn't kept on posting.
I think you need to, at least, say Html.EditorFor(...), but you probably want to use the helper method you wrote:
<%: Html.DateFor(model => model.DataNascita) %>
However, it is impossible to say exactly what is going on here without knowing the data type of the DataNascita property on the model.
I suggest you use Jquery Datepicker.
You can customize it with Jquery Themeroller.

Razor actionlink autogenerating ?length=7 in URL?

I have the link below on a razor page:
#Html.ActionLink("Create New Profile", "Create", "Profile", new { #class="toplink" })
It appears in thes source of view page as shown below:
Create New Profile
When I click on the link the URL is like this:
http://localhost:54876/admin/profile/create?length=7
I don't want ?length=7. Why is this auto generated?
The ActionLink override you are using matches to the (string linkText, string actionName, Object routeValues, Object htmlAttributes) override. So your "Profile" value is being passed to the routeValues parameter. The behavior of this function with respect to this parameter is to take all public properties on it and add it to the list of route values used to generate the link. Since a String only has one public property (Length) you end up with "length=7".
The correct overload you want to use is the (string linkText, string actionName, string controllerName, Object routeValues, Object htmlAttributes) and you call it loke so:
#Html.ActionLink("Create New Profile", "Create", "Profile", new {}, new { #class="toplink"})
I'm not sure the exact cause of this, but change it to:
#Html.ActionLink("Create New Profile", "Create", "Profile", new {}, new { #class="toplink" })
I don't know which overload MVC is picking when you leave off the last parameter (htmlattributes is the added one), but that will fix it. One of these days I'll investigate and figure out exactly what's going on.
Another thing to note, since you are defining the controller in the #ActionLink, which you may not need to do, for example, the view that your "Create New Profile" #ActionLink is expressed in might be "/admin/profile/index.cshtml", a view that lists existing profiles, in this case, you do not need to define the controller in the #ActionLink as the #ActionLink is already relative to the ProfileController, so your #ActionLink could be
#Html.ActionLink("Create New Profile", "Create", null, new { #class="toplink" })
I used null instead of new{} as the marked answer does, I think this is more appropriate myself. ActionLink overloads are not the most straightforward thing ever.

MVC form action

I have a edit View - Product/Edit/1
1 being the Id of the Product.How can I set the action of the edit post in the View to the POST edit action
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int Id, FormCollection collection)
The form tag is prepopulated as
but I want to set it to /Product/Edit/1
I am using this
<%using (Html.BeginForm()){ %>
but know its not right.Can someone help me how to set the form action using the htmlhelper class extension method to the Url in the browser
If you look at the intellisense for creating a Form with the HtmlHelper you will see there are parameters for specifying routeValues (of type object). Here you can specify the ID.
Your Edit View will be strongly typed with your Product object so you can specify Model.ID.
<% using (Html.BeginForm("Edit", "Product", new { Id = Model.ID } %>
...

Resources