How to build html using HTML helpers in MVC3 - html

I've a helper like this, I created this using raw HTML inside as follows:
private static readonly Core Db = new Core();
// Main menu
public static MvcHtmlString MainMenu()
{
IQueryable<Page> primaryPages = Db.Pages.Where(p => p.IsItShowInMenu);
var sb = new StringBuilder();
sb.Clear();
string pagecode = Convert.ToString(HttpContext.Current.Request.RequestContext.RouteData.Values["url"]);
sb.Append("<div id=\"Logo\">");
sb.Append("<span id=\"Logo_Text\">Dr. Shreekumar</span> <span id=\"Logo_Sub_Text\">Obstetrician & Gynecologist</span>");
sb.Append("</div>");
sb.Append("<div id=\"Primary_Menu\">");
sb.Append("<ul>");
foreach (Page page in primaryPages)
{
if (page.PageCode != "Home")
{
Page currentPage = Db.Pages.SingleOrDefault(p => p.PageCode == pagecode);
if (currentPage != null)
{
Page parentPage = Db.Pages.Find(currentPage.ParentId);
if (parentPage != null)
{
sb.AppendFormat((page.PageCode == parentPage.PageCode ||
page.PageCode == currentPage.PageCode)
? "<li class=\"active\">{1}</li>"
: "<li>{1}</li>", page.PageCode,
page.Name.Trim());
}
else
{
sb.AppendFormat("<li>{1}</li>", page.PageCode,page.Name);
}
}
else
{
sb.AppendFormat("<li>{1}</li>", page.PageCode, page.Name);
}
}
}
sb.Append("</ul>");
sb.Append("</div>");
return new MvcHtmlString(sb.ToString());
}
Can anybody suggest me that how can I convert this using MVC HTML helpers (helpers for anchor, list (li), div etc)

It is an important part of your role as the architect of your application to define what will be generated by helpers and what not, as it depends on what is repeated where and how often in your code. I am not going to tell you what to build helpers for because that depends on the architecture of your whole application. To help you make the decision, however, consider the two general types of helpers you can build: global and local.
Global helpers are for chunks of code which are often repeated across your site, possibly with a few minor changes that can be handled by passing in different parameters. Local helpers do the same job, but are local to a given page. A page which has a repeating segment of code that isn't really found anywhere else should implement a local helper. Now then...
Global helpers: Create a new static class to contain your helpers. Then, create static methods inside the container class that look like this:
public static MvcHtmlString MyHelper(this HtmlHelper helper, (the rest of your arguments here))
{
// Create your HTML string.
return MvcHtmlString.Create(your string);
}
What this does is create an extension method on the Html helper class which will allow you to access your helpers with the standard Html. syntax. Note that you will have to include the namespace of this class in any files where you want to use your custom helpers.
Local helpers: The other way to do helpers works when you want them to be local to a single view. Perhaps you have a block of code in a view that is being repeated over and over again. You can use the following syntax;
#helper MyHelper()
{
// Create a string
#MvcHtmlString.Create(your string here);
}
You can then output this onto your page using:
#MyHelper()
The reason why we are always creating MvcHtmlString objects is because as a security feature built into MVC, outputted strings are encoded to appear as they look in text on the page. That means that a < will be encoded so that you actually see a "<" on the page. It won't by default start an HTML tag.
To get around this, we use the MvcHtmlString class, which bypasses this security feature and allows us to output HTML directly to the page.

I suggest you move all this logic into a separate Section as it is a Menu that is being rendered.
Instead of building the HTML from the code, it is cleaner and a lot more convenient to build it using Razor's helpers. Refer to this as well as this article from Scott Gu on how to render sections to get a quick starting guide.
Consider using Helper methods such as
#Html.DropDownListFor() or
#Html.DropDownList()

Related

How to call Html.Raw and Html.Partial outside a razor page?

I have this requirements. I need to be able to write this code in my razor views:
#Filters.Render(Filters.DateRangeFilter, new DateRangeFilterParameters { });
The alternative is:
#Html.Partial("/Views/Shared/DateRangeFilter.cshtml", new DateRangeFilterParameters { });
In other words, I want Filters class to wrap Html.Partial. For that reason, I thought of this code:
public class Filters {
public const string DateRangeFilter = "/Views/Shared/DateRangeFilter.cshtml";
public static HtmlString Render(string filterPath, object parameters)
{
// Here I need to call Html.Partail, how?
}
}
To use Html.Raw within the controller you can request the injected IHtmlHelper service. E.g.:
HttpContext.RequestServices.GetService(typeof(IHtmlHelper)) as IHtmlHelper;
Or you can do your own implementation for the helper. And in order to use Html.Partial you need to use IRazorViewEngine, ViewContext, and other stuff. So basically you need to implement a service for that, and here is a good example Render Partial View To String Outside Controller Context.
I don't know if there is an easier way to achieve those, but that is what on my mind.

Bundle Helper for bundling and splitting coffeescript

I was experimenting with Bundle and Minification in MVC4 and came across an interesting problem.
I am using Coffeescript and I would like a Render helper that works a bit like the #Scripts.Render() method.
For example, let's say I have this bundle config:
new ScriptBundle("~/bundle/appfiles").Include(
"~/Scripts/app/sample.js",
"~/Scripts/app/myApp.js");
In the cshtml, when I do #Scripts.Render() I get different results based on the debug setting in the web.config. If debug is true I get one script tag per file, otherwise I get a single script tag that returns the bundled and minified js. This is fine.
Let-s assume now that I want to do the same with my Coffeescripts. I create a bundle like this:
new Bundle("~/bundle/appfiles", new CoffeeBundler(), new JsMinify()).Include(
"~/Scripts/app/sample.coffee",
"~/Scripts/app/myApp.coffee");
The problem now is that if I use #Scripts.Render() I get, while in debug, one script per file but this is not transformed at all. The only use I could do is this:
<script type="text/javascript" src="#(BundleTable.Bundles.ResolveBundleUrl("~/bundle/appfiles"))"></script>
But this will, even in debug mode, bundle everything together and then minify, which of course is not what I want.
I have tried to create a Coffee.Render() helper similar to the Scripts one but it uses the AssetManager class which is internal to the System.Web.Optimization assembly.
I was wondering if you have an idea on how to do this in a clean way (i.e: using the available public classes, not copying and pasting the whole AssetManager code, not doing fancy Directory.EnumerateFiles when creating the bundle).
Thanks!
PS: I know that a quicker solution would be to use Mindscape Workbench and bundle the generated js files, I am looking for something that uses what the framework has, maybe also avoiding to have to tell the team to install a tool that people may not like...
In the end I went for a HtmlHelper solution for this. Still in early stage but working as I would like. It is detailed in a blog post for the time being.
Here is the full Helper code in case the blog goes lost...
public static class HtmlHelperExtensions
{
public static MvcHtmlString RenderCoffeeBundle(this HtmlHelper htmlHelper, string virtualPath)
{
if (String.IsNullOrEmpty(virtualPath))
throw new ArgumentException("virtualPath must be defined", "virtualPath");
var list = GetPathsList(virtualPath);
//TODO: Nice and cleaner EliminateDuplicatesAndResolveUrls(list);
var stringBuilder = new StringBuilder();
foreach (string path in list)
{
stringBuilder.Append(RenderScriptTag(path));
stringBuilder.Append(Environment.NewLine);
}
return MvcHtmlString.Create(stringBuilder.ToString());
}
private static IEnumerable<string> GetPathsList(string virtualPath)
{
var list = new List<string>();
if (BundleResolver.Current.IsBundleVirtualPath(virtualPath))
{
if (!BundleTable.EnableOptimizations)
{
foreach (var path in BundleResolver.Current.GetBundleContents(virtualPath))
{
var bundlePath = "~/autoBundle" + ResolveVirtualPath(path.Replace("coffee", "js"));
BundleTable.Bundles.Add(new Bundle(bundlePath, new CoffeeBundler()).Include(path));
// TODO: Get the actual CustomTransform used in the Bundle
// rather than forcing "new CoffeeBundler()" like here
list.Add(bundlePath);
}
}
else
list.Add(BundleResolver.Current.GetBundleUrl(virtualPath));
}
else
list.Add(virtualPath);
return list.Select(ResolveVirtualPath).ToList();
}
private static string RenderScriptTag(string path)
{
return "<script src=\"" + HttpUtility.UrlPathEncode(path) + "\"></script>";
}
private static string ResolveVirtualPath(string virtualPath)
{
return VirtualPathUtility.ToAbsolute(virtualPath);;
}
}
I'm sorry I'm not addressing your exact question, but I do want to speak to your PS at the end of the post.
At this time, I don't really think we have a "no tools" story, but I do agree with the sentiment of "using what the framework has".
To that end, I would strongly recommend using TypeScript. You don't have to learn a new language (like you do with CoffeeScript) and yet it gives you a strongly-typed version of JavaScript that you can use a lot more like c# (with type validation etc.).
It will take you 20 mins to go through some of the tutorials:
http://www.typescriptlang.org/Playground/
Or, better yet, have a look at the BUILD session from the fall:
http://channel9.msdn.com/Events/Build/2012/3-012
Btw...if this isn't a direction you are wanting to go, no worries...I just find a lot of devs don't even know about TypeScript yet as an option.
Hope this helps in your quest to simplify things for your team.
Cheers.

MVC Razor syntax: # followed by HTML

I came across this code today and don't really understand it. Please could someone tell me what this means and how to interpret it? I have simplified it but it's basically the # symbol followed by some HTML.
The call is:
#Html.Tmpl(#<p>text to display</p>)
The function is:
public static HelperResult Tmpl<TModel>( this HtmlHelper<TModel> html, Func<HtmlHelper<TModel>, HelperResult> template )
{
return new HelperResult( writer => template( html ).WriteTo( writer ) );
}
Please enlighten me. Thank you.
This is an example of what is known as a Templated Razor Delegate. Quite simply, it is a type of HTML helper which accepts a block of Razor template code which can be used to compose the result of a complex operation.
A simple use case might be an Html.List(data, template) method which accepts a list of records and a template for each row of data. The template markup is a delegate which can be invoked and passed a model within the helper's logic.
public static HelperResult List<T>(this IEnumerable<T> items,
Func<T, HelperResult> template) {
return new HelperResult(writer => {
foreach (var item in items) {
template(item).WriteTo(writer);
}
});
}
Phil Haacked goes into more detail here: http://haacked.com/archive/2011/02/27/templated-razor-delegates.aspx.

How do I use content containing razor markup as a parameter for a method?

What I am trying to do is to be able to write javascript blended with razor styled markup and methods inside .cshtml file and send this to a separate method for usage later on.
My .cshtml looks something like this:
#{SomeClass.SaveForLater(#<script type="text/javascript">window.alert('#Model.SomeParamter')}</script>);
And inside the class SomeClass:
public static void SaveForLater(HtmlString str) {
// will be using str.ToString() here and save the string output for use later on.
}
But what I receive is this error message:
CS1660: Cannot convert lambda expression to type 'System.Web.HtmlString' because it is not a delegate type
Am I using the wrong type for the argument or do I need to rethink the whole concept?
Solution
Thanks to SLaks below I ended up doing this:
public static void SaveForLater<T>(Func<T, HelperResult> template, dynamic model)
{
// template(model).ToHtmlString()
}
Using it like this:
#{SomeClass.SaveForLater<SomeModel>(
#<script type="text/javascript">window.alert('#Model.SomeParamter')</script>,
Model
);
You're trying to take an inline helper.
You need to accept a Func<Something, HelperResult>.

Spring MVC Request URLs in JSP

I am writing a web application using Spring MVC. I am using annotations for the controllers, etc. Everything is working fine, except when it comes to actual links in the application (form actions, <a> tags, etc.) Current, I have this (obviously abbreviated):
//In the controller
#RequestMapping(value="/admin/listPeople", method=RequestMethod.GET)
//In the JSP
Go to People List
When I directly enter the URL like "http://localhost:8080/MyApp/admin/listPeople", the page loads correctly. However, the link above does not work. It looses the application name "MyApp".
Does anyone know if there is a way to configure Spring to throw on the application name on there?
Let me know if you need to see any of my Spring configuration. I am using the standard dispatcher servlet with a view resolver, etc.
You need to prepend context path to your links.
// somewhere on the top of your JSP
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
...
Go to People List
The c:url tag will append the context path to your URL. For example:
<c:url value="/admin/listPeople"/>
Alternately, I prefer to use relative URLs as much as possible in my Spring MVC apps as well. So if the page is at /MyApp/index, the link <a href="admin/listPeople"> will take me to the listPeople page.
This also works if you are deeper in the URL hierarchy. You can use the .. to traverse back up a level. So on the page at/MyApp/admin/people/aPerson, using <a href="../listPeople"> will like back to the list page
I prefer to use BASE tag:
<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/" />
Then, all your links can be like:
Go to People List
As i have just been trying to find the answer to this question and this is the first google result.
This can be done now using the MvcUriComponentsBuilder
This is part of the 4.0 version of Spring MVC
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.html
The method needed is fromMappingName
From the documentation :
Create a URL from the name of a Spring MVC controller method's request mapping.
The configured HandlerMethodMappingNamingStrategy determines the names of controller method request mappings at startup. By default all mappings are assigned a name based on the capital letters of the class name, followed by "#" as separator, and then the method name. For example "PC#getPerson" for a class named PersonController with method getPerson. In case the naming convention does not produce unique results, an explicit name may be assigned through the name attribute of the #RequestMapping annotation.
This is aimed primarily for use in view rendering technologies and EL expressions. The Spring URL tag library registers this method as a function called "mvcUrl".
For example, given this controller:
#RequestMapping("/people")
class PersonController {
#RequestMapping("/{id}")
public HttpEntity getPerson(#PathVariable String id) { ... }
}
A JSP can prepare a URL to the controller method as follows:
<%# taglib uri="http://www.springframework.org/tags" prefix="s" %>
Get Person
I usually configure tomcat to use context root of "/" or deploy the war as ROOT.war. Either way the war name does not become part of the URL.
You could use a servletRelativeAction. I'm not sure what versions this is available in (I'm using 4.0.x currently) and I haven't seen much documentation on this, but if you look at the code backing the spring form you can probably guess. Just make sure the path you pass it starts with a "/".
Example:
<form:form class="form-horizontal" name="form" servletRelativeAction="/j_spring_security_check" method="POST">
See org.springframework.web.servlet.tags.form.FormTag:
protected String resolveAction() throws JspException {
String action = getAction();
String servletRelativeAction = getServletRelativeAction();
if (StringUtils.hasText(action)) {
action = getDisplayString(evaluate(ACTION_ATTRIBUTE, action));
return processAction(action);
}
else if (StringUtils.hasText(servletRelativeAction)) {
String pathToServlet = getRequestContext().getPathToServlet();
if (servletRelativeAction.startsWith("/") && !servletRelativeAction.startsWith(getRequestContext().getContextPath())) {
servletRelativeAction = pathToServlet + servletRelativeAction;
}
servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction));
return processAction(servletRelativeAction);
}
else {
String requestUri = getRequestContext().getRequestUri();
ServletResponse response = this.pageContext.getResponse();
if (response instanceof HttpServletResponse) {
requestUri = ((HttpServletResponse) response).encodeURL(requestUri);
String queryString = getRequestContext().getQueryString();
if (StringUtils.hasText(queryString)) {
requestUri += "?" + HtmlUtils.htmlEscape(queryString);
}
}
if (StringUtils.hasText(requestUri)) {
return processAction(requestUri);
}
else {
throw new IllegalArgumentException("Attribute 'action' is required. " +
"Attempted to resolve against current request URI but request URI was null.");
}
}
}
Since it's been some years I thought I'd chip in for others looking for this. If you are using annotations and have a controller action like this for instance:
#RequestMapping("/new") //<--- relative url
public ModelAndView newConsultant() {
ModelAndView mv = new ModelAndView("new_consultant");
try {
List<Consultant> list = ConsultantDAO.getConsultants();
mv.addObject("consultants", list);
} catch (Exception e) {
e.printStackTrace();
}
return mv;
}
in your .jsp (view) you add this directive
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
and simply use
<spring:url value="/new" var="url" htmlEscape="true"/>
New consultant
where
value's value should match #RequestMapping's argument in the controller action and
var's value is the name of the variable you use for href
HIH