I am using the Telerik RadEditor (Q1 2009 SP1) in our ASP.NET MVC (RTM) project. The editor works great when rendered as a hardcoded object on the page with a static id. But when extending with an HtmlHelper to do dynamic creation by passing in an Id it seems to render the html as all lowercase for the tag. Does the HtmlHelper object mess with this innately by chance? The attributes look upper and lowercase respectively but this seems strange. Here is my code....thanks in advance!
<% if (placeholder.Type.ToLower() == "richtext") { %>
<%= Html.RadEditor("placeholder_" + placeholder.Name) %>
<% } else { %>
<%= Html.TextBox("placeholder_" + placeholder.Name, null, new { #class = placeholder.Type }) %>
<% } %>
The helper looks like this....
public static string RadEditor(this HtmlHelper html, string Id)
{
var sb = new StringBuilder();
sb.Append("<telerik:RadEditor ID='" + Id + "' Runat='server' DialogHandlerUrl='~/Telerik.Web.UI.DialogHandler.axd'>");
sb.Append("<Content>");
sb.Append("</Content>");
sb.Append("</telerik:RadEditor>");
return sb.ToString();
}
For the time being you cannot render RadEditor without having a valid Page object with a ScriptManager. We (Telerik that is) plan to add support for "standalone" rendering in the near future. Should be announced in a blog post so stay tuned.
The problem is the tag is a server side control. When you place it hardcoded in your page, the server side tag gets translated to html. When you're using the htmlhelper, you're outputting the html and it doesn't get processed as a server side tag.
If you want to do something dynamic, you should use a UserControl (.ascx file) and then use the Html.RenderPartial method.
Related
Short of overriding ViewData.TemplateInfo.HtmlFieldPrefix with an empty string, is there a way to keep the prefix from coming through on a nested set of strongly-typed EditorFor or DisplayFor helper calls?
Here's the ugly markup/code I have working so far:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Web.Mvc.SelectList>" %>
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
<%=Html.DropDownList("sort", Model)%>
I've tried the *For overload that allows specifying htmlFieldName but that only changes the immediate level. If I have a prefix at the point of that call, I just change what is appended to the prefix.
I could write the template markup by hand, but doing so for a SelectList object seems like I will just end up copying over the MVC source with a single tweak since it involves object data binding logic.
If you have a property on your view model, rather than just doing dropdown list directly on the model, you can put a DataAnnontation attribute on it.
public class MyModel
{
[Display(Name="Your Favorite Choices")]
public string[] Choices {get; set;}
}
then in your code
<%= Html.LableFor(m => m.Choices) %><br />
<%=Html.DropDownList("sort", Model.Choices)%>
Should use that name.
[Hope I got it right from memory. :)]
Here's the MSDN link:
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.displayattribute.aspx
Not sure if I understand your question, but, if you render the "child" views as partials instead of EditorFor then the fields will not be prefixed.
Is there a good reason to use the strongly typed html helper...
<%: Html.DisplayTextFor(model => model.Email) %>
As opposed to...
<%: Model.Email %>
Consider the following Model:
public class MyModel
{
public string Name { get; set; }
[DisplayFormat(NullDisplayText = "No value available!")]
public string Email { get; set; }
}
in my view:
<%= Html.DisplayTextFor(m => m.Email) %>
<%: Model.Email %>
The first line will display "No value available" if we leave the Email to be 'null' whereas the second line will not display anything.
Conclusion:
Html.DisplayTextFor will take into consideration the DataAnnotations on your properties, <%: Model.Email %> will not.
Also <%: Model.Email %> will throw an "Object reference error" when the value is null, but <%= Html.DisplayTextFor %> wont.
DisplayTextFor will also get called during "DisplayFor" and "EditFor" execution. This will ensure that any templated helpers will display the text using the correct Templated Helpers if set ... so a change to the single templated helper will propogate through all displays of that text item ... simple display, edit form, create forms etc etc.
Well, DisplayTextFor will not break if you do not pass in a model (or pass null). Model.Email will throw an exception on null. So, DisplayTextFor is more robust.
The point of the strongly typed helpers is to allow greater compile time checking. This is very handy when refactoring.
DisplayTextFor allows using a consistent construct (with the other strongly typed helpers) throughout the page. Some people might find that more appealing.
DisplayTextFor also allows you to pass the names of templates and fields as parameters.
I'm trying to create a custom HTML Helper to help simplify my masterpages menu, however it is not rendering on the HTML when I use it.. I'm thinking I will need to create a partial view, any ideas?
I did this..
public static string CreateAdminMenuLink(this HtmlHelper helper, string caption, string link)
{
var lnk = TagBuilder("a");
lnk.SetInnerText(caption);
lnk.MergeAttribute("href", target);
return lnk.ToString(TagRenderMode.SelfClosing);
}
Now in my View, i have
<% Html.CreateAdminMenuLink("Home", "~/Page/Home"); %>
Thanks: Dave Swersky
Fix was: I forgot the equals and removed the semi-colon
<%= Html.CreateAdminMenuLink("Home", "~/Page/Home") %>
but when I look at the source, its empty.. tried adding <% using (Html.BeginForm()) %> and it adds a form.. but the link still doesnt come up.. debugged and the string works when i look at the watch, but does not render..
Any ideas?
Modify your markup:
<%= Html.CreateAdminMenuLink("Home", "~/Page/Home") %>
The equals sign and no semicolon should do the trick.
So let's say I have a view that access a date:
<%= Html.TextBoxFor(model => Model.Birthday) %>
How do I have my view dictate how that date is formatted? I'd like to use common formatting options, and it's important that this is 2-way (both for display and data-entry).
In your model, you could use System.ComponentModel.DataAnnotations metadata to specify the format you want the property in. e.g.
[DisplayFormat(DataFormatString="{0:MM/dd/yyyy}")]
public DateTime Birthday { get; set; }
Then, in your view, use:
<%= Html.EditorFor(model => model.Birthday) %>
Unfortunately, the regular HTML Helpers don't respect the metadata, so you have to use Html.EditorFor.
Use a specific ViewModel class that has a string in the format you want for the birthday and use that to display in the textbox. When you post the form, if you are using default binding for the domain object itself, it should parse the string for you. If you are using a ViewModel, you'll have to parse it upon posting the form.
I was kind of surprised to see that you can't just throw a format string in Html.EditorFor(). I've sinced starting using EditorTemplates.
Create a directory structure in your solution.
Views > Shared > EditorTemplates
Add a new MVC 2 User Control in the EditorTemplates folder and name it DateTime.ascx. Html.EditorFor(DateTime) will now use this user control for display.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>
<%= Html.TextBox(string.Empty, (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty)) %>
I have a form rendered via Html.BeginForm(), it exists as a component in the Master page so that it appears on every page in the application. I have done this using Html.RenderAction() from Mvc Futures assembly. It's a simple search form that updates some items in the same component underneigh the search form itself, and performs a GET so that the search term appears in the querystring.
<div class="sideBarContent">
<h2>Search Products</h2>
<% using (Html.BeginForm(ViewContext.RouteData.Values["action"].ToString(),
ViewContext.RouteData.Values["controller"].ToString(), FormMethod.Get)) { %>
<fieldset>
<legend>Search Products</legend>
<div class="formRow">
<label for="ProductsSearch">Search</label>
<%= Html.TextBox("ProductsSearch") %>
</div>
<input type="submit" value="Search" class="button" />
</fieldset>
<% } %>
<ul>
// Products will eventually be listed here
</ul>
</div>
I need this form to do the following:
1) It should perform a GET to whatever current page it is on appending 'ProductsSearch' as a querystring parameter (eg. example.com/?ProductsSearch=test or example.com/books/fiction?ProductsSearch=test)
2) It should remember any exising querystring parameters that are already in the querystring, maintaining them after you click Search button eg. example.com/myOrders?page=2 after Search click it should go to example.com/myOrders?page=2&ProductsSearch=test)
I can get it to do 1) but can't work out 2).
I relise that normally for a from to GET and appending querystring params it needs to have hidden form fields, so I could write a utility function that automatically adds a bunch of hidden form fields for any querystring values, but I wanted to check that there's wasn't an easier approach, or maybe I'm going about it the wrong way.
Cheers!
You'll need to do the hidden form field method.
Even if you could attach the entire querystring to the end of the URL in the action attribute of the <form> tag, browsers don't pay attention to this when doing GET form submissions.
Your method isn't too difficult; you'd want to do something like this:
public static string QueryStringAsHidden(this HtmlHelper helper)
{
var sb = new StringBuilder();
foreach (var key in HttpContext.Current.Request.QueryString.AllKeys)
{
if (! key.StartsWith("ProductSearch"))
sb.Append(helper.Hidden(key, HttpContext.Current.Request.QueryString[key]));
}
return sb.ToString();
}
I put the .StartsWith() in there because you don't want to be on a search page and submit the search string twice (and now you can prepend paging and other search-specific variables with ProductSearch.
Edit: PS: To get the form to post to the current page, you don't have to explicitly provide action and controller -- you can also send nulls.
Edit2: Why even bother with a helper method? :)
<% HttpContext.Current.Request.QueryString.AllKeys.Where(k => !k.StartsWith("ProductSearch")).ToList().ForEach(k => Response.Write(Html.Hidden(k, HttpContext.Current.Request.QueryString[k]))); %>
James
A direct to call BeginForm() does keep your query string values. Any other overload tends to fail. I love the ease of using BeginForm() from my forms, but needed a way to class all my styled forms a certain way an not lose the query string values in the action.
Here is what I came up with:
public static MvcForm BeginNormalForm<T>(this HtmlHelper<T> htmlHelper)
{
var dictionary = new Dictionary<string, object> {{"class", "normal"}};
var rvd = new RouteValueDictionary();
if (htmlHelper.ViewContext.HttpContext != null && htmlHelper.ViewContext.HttpContext.Request != null)
{
foreach (var key in htmlHelper.ViewContext.HttpContext.Request.QueryString.AllKeys)
{
rvd[key] = htmlHelper.ViewContext.HttpContext.Request.QueryString[key];
}
}
var form = htmlHelper.BeginForm(null, null, rvd, FormMethod.Post, dictionary);
return form;
}
Seems to work well and keeps my class attribute.
Use one of the overloads of BeginForm that takes a routeValues object or dictionary.
Additional properties not in the route will be added as query parameters.