Asp.net mvc 2 templates without prefix - asp.net-mvc

Given following view model:
class DetailsViewModel
{
public HeaderViewModel Header {get;set;}
public FooterViewModel Footer {get;set;}
}
I'm using editor template for Header view model:
<%: Html.EditorFor(x => x.Header) %>
The editor template (EditorTemplates/HeaderViewModel.ascx)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HeaderViewModel>" %>
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
<%: Html.EditorFor(x => x.Search) %>
The result:
<input type="text" value="" name="Search" id="Search" />
If I remove the line
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
the result is:
<input type="text" value="" name="Header.Search" id="Header_Search" />
Is there another way to achieve the same - render the names of the control without prefix?
I was thinking about a helper:
public static MvcHtmlString EditorWithoutPrefix<TModel, TValue>(
this HtmlHelper<TModel> html, TValue value)
{
var htmlHelper =... // create new HtmlHelper<TValue> and set it's model to be 'value' argument
return htmlHelper.EditorForModel();
}
and use it:
<%: Html.EditorWithoutPrefix(Model.Header) %>
but it is throwing exceptions.
Or maybe you know another elegant way to render names without prefix?

You could use the proper overload:
<%: Html.EditorFor(x => x.Search, "SearchViewModel", "") %>

Related

html.actionlink take C# variable as parameter

<% foreach (var item in Model) { %>
<table width="100%" class="topicContainer">
<tr>
<td> <%: Html.DisplayFor(modelItem => item.headerclob) %></td>
</tr>
<tr>
<td><%: Html.ActionLink("ViewTopic", "ViewTopic","Forum" ,
new { id=item.topicId },null) %></td>
</tr>
</table>
<% } %>
I want of link ViewTopic item.headerclob should be displayed in hyperlink without using Razor.
I also want to apply css to it.
I think the below code should work
<%: Html.ActionLink("item.headerclob, "ViewTopic","Forum" ,
new { id=item.topicId },null) %>
it use the following format
public static string ActionLink(this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
object values,
object htmlAttributes)
if you are using MVC 3 then the you can just use "item.topicId" instead of "id = item.topicId"
Edited
yes it works but after removing semicolon from item.headerClob
<%: Html.ActionLink(item.headerclob, "ViewTopic","Forum" ,
new { id=item.topicId },null) %>
Edit
add a class to action link then use your css file for setting necessary properties
<%: Html.ActionLink(item.headerclob, "ViewTopic","Forum" ,
new { id=item.topicId , #class = "YourClass"},null) %>
now you can apply css properties to the action link you set css properties to others
EDIT
If you do not want to use razor, I could suggest you to build anchors by your self like following
item.headerclob

Razor: Cannot render Html.Label helper in a #Section (outputting source only)

I was unable to figure out how to use #class inside Html.LabelFor, so I updated an extension helper for Html.Label with code found on SO.
My LabelExtension.cs file has the following class:
public static class LabelExtensions
{
public static string Label(this HtmlHelper helper,
string labelFor,
string value,
object htmlAttributes)
{
TagBuilder labelBuilder = new TagBuilder("label");
if (!string.IsNullOrEmpty(labelFor))
{
labelBuilder.Attributes.Add("for", labelFor);
}
labelBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
labelBuilder.SetInnerText(value);
return labelBuilder.ToString(TagRenderMode.Normal);
}
}
I originally used the following in a MVC2 .aspx page thusly (which I am converting to .cshtml):
<% Html.BeginForm(); %> <%= Html.Label("txt_name_udq", "Your First Name (required):",
new {
#class
= "mylabelstyle" } )%>
<br />
<%= Html.TextBox("txt_name_udq", null, new {
#class
= "myinputstyle" } )%>
<br />
<%= Html.Label("txt_email_udq", "Your E-Mail Address (required):", new {
#class
= "mylabelstyle" } )%>
<br />
<%= Html.TextBox("txt_email_udq", null, new {
#class
= "myinputstyle" })%>
<br />
<% Html.EndForm(); %>
This rendered very well in MVC2 (I left out usings and such for brevity) . However, today while converting to .cshtml (using a _layout.cshtml) I found that the Html.Label is not rendering and instead outputting source. Here is the code:
#section QuoteForm
{
<div class="entryfull">
<div class="entryfull_box">
<div class="entryfull_text">
<h2>
Quote Request
</h2>
<ul class="ul-check">
<li>Free</li>
<li>Confidential</li>
<li>Anonymous</li>
<li>No Obligation</li>
</ul>
#using (Html.BeginForm())
{
#Html.Label("txt_name_udq", "Your First Name (required):", new { #class = "mylabelstyle" })
<br />
#Html.TextBox("txt_name_udq", null, new { #class = "myinputstyle" })
<br />
#Html.Label("txt_email_udq", "Your E-Mail Address (required):", new { #class = "mylabelstyle" })
<br />
#Html.TextBox("txt_email_udq", null, new { #class = "myinputstyle" })
<br />
}
</div>
</div>
</div>
}
The above is just something simple, not final product. I am just trying to get it to render first. Note: 1) I tried various iterations of Html.BeginForm; and 2) I even enclosed it in . Still, its not "working".
Here is what you see on the browser for the label (source outputted in browswer), right above the textbox (which renders):
<label class="mylabelstyle" for="txt_name_udq">Your First Name (required):</label>;
And if you "View Source", here is what you see:
<label class="mylabelstyle" for="txt_name_udq">Your First Name (required):</label>;
Is this something to do with #section? Or is it that I am trying to use an extension from MVC2?
Thanks in advance for any thoughts/advice.
EDIT: This is the code I reference in the comment that worked in my situation. Thanks for the help.
public static MvcHtmlString Label(this HtmlHelper htmlHelper, string labelFor, string value,
object htmlAttributes)
{
var tagBuilder = new TagBuilder("label");
tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
tagBuilder.MergeAttribute("for", labelFor.Replace(".", tagBuilder.IdAttributeDotReplacement), true);
tagBuilder.SetInnerText(value);
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
That is because your helper is returning a string and that is encoded by default. Your helper should return MvcHtmlString and change the return statement to
return new MvcHtmlString(labelBuilder.ToString(TagRenderMode.Normal));

Custom Helper using the model

I'm pretty new to MVC and just read an article about helpers. Now I have this code on the View:
<div class="display-label">Ingredients:
<% foreach (var e in Model.Products_Ingredients)
{%>
<%: e.Ingredient.Name%><br />
<%: e.Percentage%>
<%if (e.Percentage != null)
{%>
%
<%}%>
<br />
<%}%>
</div>
How do I go on and create a Helper that would replace that code with something simpler like:
<div class="display-label">Ingredients: <%: MyHelpers.Ingredients %> </div>
Thank you!
you'll need to make an HtmlHelper Extension Method
public namespace User.Extensions
public static HtmlHelperExtensions
{
public static string Ingredients(this HtmlHelper, Product_Ingredients productIngredients)
{
string result = string.Empty;
// loop through your ingredients and build your result, could use TagBuilder, too
return result;
}
}
}
Then you can call <%=Html.Ingredients(Model.Products_Ingredients) %>
make sure you add this assembly reference to the page
<%# Import Namespace=User.Extensions" %>
or to your Web.Config so all pages have access
<pages>
<namespaces>
<add namespace="User.Extensions" />
public class MyHelpers
{
public static string Ingredients(IEnumerable<Products_Ingredients> pi)
{
//html code as string
// <%: pi.Ingredient.Name%><br />
// <%: pi.Percentage%>
// <%if (pi.Percentage != null)
// {%>
// %
// <%}%>
// <br />
return htmlCode;
}
}
In your page add
<%# Import Namespace=namespace.MyHelpers" %>
<div class="display-label">Ingredients: <%: MyHelpers.Ingredients(Model.Products_Ingredients) %> </div>

ASP.NET MVC partial views: input name prefixes

Suppose I have ViewModel like
public class AnotherViewModel
{
public string Name { get; set; }
}
public class MyViewModel
{
public string Name { get; set; }
public AnotherViewModel Child { get; set; }
public AnotherViewModel Child2 { get; set; }
}
In the view I can render a partial with
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
In the partial I'll do
<%= Html.TextBox("Name", Model.Name) %>
or
<%= Html.TextBoxFor(x => x.Name) %>
However, the problem is that both will render name="Name" while I need to have name="Child.Name" in order for model binder to work properly. Or, name="Child2.Name" when I render the second property using the same partial view.
How do I make my partial view automatically recognize the required prefix? I can pass it as a parameter but this is too inconvenient. This is even worse when I want for example to render it recursively. Is there a way to render partial views with a prefix, or, even better, with automatic reconition of the calling lambda expression so that
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
will automatically add correct "Child." prefix to the generated name/id strings?
I can accept any solution, including 3-rd party view engines and libraries - I actually use Spark View Engine (I "solve" the problem using its macros) and MvcContrib, but did not find a solution there. XForms, InputBuilder, MVC v2 - any tool/insight that provide this functionality will be great.
Currently I think about coding this myself but it seems like a waste of time, I can't believe this trivial stuff is not implemented already.
A lot of manual solutions may exists, and all of them are welcome. For example, I can force my partials to be based off IPartialViewModel<T> { public string Prefix; T Model; }. But I'd rather prefer some existing/approved solution.
UPDATE: there's a similar question with no answer here.
You can extend Html helper class by this :
using System.Web.Mvc.Html
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = name
}
};
return helper.Partial(partialViewName, model, viewData);
}
and simply use it in your views like this :
<%= Html.PartialFor(model => model.Child, "_AnotherViewModelControl") %>
and you will see everything is ok!
so far, i was searching for the same thing I have found this recent post:
http://davybrion.com/blog/2011/01/prefixing-input-elements-of-partial-views-with-asp-net-mvc/
<% Html.RenderPartial("AnotherViewModelControl", Model.Child, new ViewDataDictionary
{
TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Child1" }
})
%>
My answer, based on the answer of Mahmoud Moravej including the comment of Ivan Zlatev.
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
StringBuilder htmlFieldPrefix = new StringBuilder();
if (helper.ViewData.TemplateInfo.HtmlFieldPrefix != "")
{
htmlFieldPrefix.Append(helper.ViewData.TemplateInfo.HtmlFieldPrefix);
htmlFieldPrefix.Append(name == "" ? "" : "." + name);
}
else
htmlFieldPrefix.Append(name);
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = htmlFieldPrefix.ToString()
}
};
return helper.Partial(partialViewName, model, viewData);
}
Edit:
The Mohamoud's answer is incorrect for nested partial rendering. You need to append the new prefix to the old prefix, only if it is necessary. This was not clear in the latest answers (:
Using MVC2 you can achieve this.
Here is the strongly typed view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcLearner.Models.Person>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Create</h2>
<% using (Html.BeginForm()) { %>
<%= Html.LabelFor(person => person.Name) %><br />
<%= Html.EditorFor(person => person.Name) %><br />
<%= Html.LabelFor(person => person.Age) %><br />
<%= Html.EditorFor(person => person.Age) %><br />
<% foreach (String FavoriteFoods in Model.FavoriteFoods) { %>
<%= Html.LabelFor(food => FavoriteFoods) %><br />
<%= Html.EditorFor(food => FavoriteFoods)%><br />
<% } %>
<%= Html.EditorFor(person => person.Birthday, "TwoPart") %>
<input type="submit" value="Submit" />
<% } %>
</asp:Content>
Here is the strongly typed view for the child class (which must be stored in a subfolder of the view directory called EditorTemplates):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcLearner.Models.TwoPart>" %>
<%= Html.LabelFor(birthday => birthday.Day) %><br />
<%= Html.EditorFor(birthday => birthday.Day) %><br />
<%= Html.LabelFor(birthday => birthday.Month) %><br />
<%= Html.EditorFor(birthday => birthday.Month) %><br />
Here is the controller:
public class PersonController : Controller
{
//
// GET: /Person/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
return View();
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create()
{
Person person = new Person();
person.FavoriteFoods.Add("Sushi");
return View(person);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
return View(person);
}
}
Here are the custom classes:
public class Person
{
public String Name { get; set; }
public Int32 Age { get; set; }
public List<String> FavoriteFoods { get; set; }
public TwoPart Birthday { get; set; }
public Person()
{
this.FavoriteFoods = new List<String>();
this.Birthday = new TwoPart();
}
}
public class TwoPart
{
public Int32 Day { get; set; }
public Int32 Month { get; set; }
}
And the output source:
<form action="/Person/Create" method="post"><label for="Name">Name</label><br />
<input class="text-box single-line" id="Name" name="Name" type="text" value="" /><br />
<label for="Age">Age</label><br />
<input class="text-box single-line" id="Age" name="Age" type="text" value="0" /><br />
<label for="FavoriteFoods">FavoriteFoods</label><br />
<input class="text-box single-line" id="FavoriteFoods" name="FavoriteFoods" type="text" value="Sushi" /><br />
<label for="Birthday_Day">Day</label><br />
<input class="text-box single-line" id="Birthday_Day" name="Birthday.Day" type="text" value="0" /><br />
<label for="Birthday_Month">Month</label><br />
<input class="text-box single-line" id="Birthday_Month" name="Birthday.Month" type="text" value="0" /><br />
<input type="submit" value="Submit" />
</form>
Now this is complete. Set a breakpoint in the Create Post controller action to verify. Don't use this with lists however because it wont work. See my question on using EditorTemplates with IEnumerable for more on that.
This is an old question, but for anyone arriving here looking for a solution, consider using EditorFor, as suggested in a comment in https://stackoverflow.com/a/29809907/456456. To move from a partial view to an editor template, follow these steps.
Verify that your partial view is bound to ComplexType.
Move your partial view to a subfolder EditorTemplates of the current view folder, or to the folder Shared. Now, it is an editor template.
Change #Html.Partial("_PartialViewName", Model.ComplexType) to #Html.EditorFor(m => m.ComplexType, "_EditorTemplateName"). The editor template is optional if it's the only template for the complex type.
Html Input elements will automatically be named ComplexType.Fieldname.
PartailFor for asp.net Core 2 in case someone needs it.
public static ModelExplorer GetModelExplorer<TModel, TResult>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression)
{
if (expression == null)
throw new ArgumentNullException(nameof(expression));
return ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
}
public static IHtmlContent PartialFor<TModel, TResult>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TResult>> expression, string partialViewName, string prefix = "")
{
var modelExplorer = helper.GetModelExplorer(expression);
var viewData = new ViewDataDictionary(helper.ViewData);
viewData.TemplateInfo.HtmlFieldPrefix += prefix;
return helper.Partial(partialViewName, modelExplorer.Model, viewData);
}
As stated here: https://stackoverflow.com/a/58943378/3901618 - for ASP.NET Core - you can use the partial tag helper.
<partial name="AnotherViewModelControl" for="Child" />
<partial name="AnotherViewModelControl" for="Child2" />
It generates all required name prefixes.
I came across this issue also and after much pain i found it was easier to redesign my interfaces such that i didn't need to post back nested model objects. This forced me to change my interface workflows: sure i now require the user to do in two steps what i dreamed of doing on one, but the usability and code maintainability of the new approach is of greater value to me now.
Hope this helps some.
You could add a helper for the RenderPartial which takes the prefix and pops it in the ViewData.
public static void RenderPartial(this HtmlHelper helper,string partialViewName, object model, string prefix)
{
helper.ViewData["__prefix"] = prefix;
helper.RenderPartial(partialViewName, model);
}
Then a further helper which concatenates the ViewData value
public static void GetName(this HtmlHelper helper, string name)
{
return string.Concat(helper.ViewData["__prefix"], name);
}
and so in the view ...
<% Html.RenderPartial("AnotherViewModelControl", Model.Child, "Child.") %>
in the partial ...
<%= Html.TextBox(Html.GetName("Name"), Model.Name) %>
Like you, I add Prefix property (a string) to my ViewModels which I append before my model bound input names. (YAGNI preventing the below)
A more elegant solution might be a base view model that has this property and some HtmlHelpers that check if the view model derives from this base and if so append the prefix to the input name.
Hope that helps,
Dan
How about just before you call RenderPartial you do
<% ViewData["Prefix"] = "Child."; %>
<% Html.RenderPartial("AnotherViewModelControl", Model.Child) %>
Then in your partial you have
<%= Html.TextBox(ViewData["Prefix"] + "Name", Model.Name) %>

Partial View with parametrized prefix for controls names

I have a BarEditor.ascx, that can be called from diffent places.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyApp.Models.Bar>" %>
<%= Html.TextBox("a") %>
...
Now consider I need to edit two objects on one page Edit.aspx
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", ViewData["bar"]); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", ViewData["baz"]); %>
</div>
<input type="submit" value="Submit" />
</form>
This submits:
a=1&a=2
I need it to be:
bar.a=1&baz.a=2
So we can process it with
public ActionResult Update(Bar bar, Bar baz)
{
...
}
What is a best way to write reusable BarEditor.ascx that can generate prefixes for controls names?
just create a ViewModel class for your BarEditor and make it strongly typed to this new class
e.g.
namespace ViewModel {
public class BarEditor {
string Prefix { get; set; }
Models.Bar Bar { get; set; }
}
}
now you create your textbox in BarEditor.ascx like this
<%= Html.TextBox(Model.Prefix + ".a") %>
and in your view you include the BarEditor like that
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", new ViewModel.BarEditor { Prefix = "Bar", Bar = ViewData["bar"]}); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", new ViewModel.BarEditor { Prefix = "Baz", Bar = ViewData["baz"]}); %>
</div>
<input type="submit" value="Submit" />
</form>
hth
I would pass a string ("baz" or "bar", etc) with my ViewData when calling the user control. Have the html.textbox get its name from the text passed and its value from the value passed.
Why not create a model for the view? Your view would then need to be a strongly typed view using the data class FormView.
public class FormView
{
string Bar {get; set;}
string Baz {get; set;}
}
Then in your view you can use
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", Model.Bar); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", Model.Baz); %>
</div>
<input type="submit" value="Submit" />
</form>
Your controller becomes
public ActionResult Update(FormView MyForm)
{
... = MyForm.Bar;
... = MyForm.Baz;
}
You should to learn about Model Mapping in ASP.Net MVC. Everything in the asp.net mvc page will be rendered to html control therefore don't distinguish between controls in
<% Html.RenderPartial("BarEditor", ViewData["bar"]); %>
and
<% Html.RenderPartial("BarEditor", ViewData["baz"]); %>

Resources