How to use ASP.NET MVC Html Helpers from a custom helper? - asp.net-mvc

I have several pages listing search results, for each result I would like to display I want to create a custom View Helper in order to avoid duplicating the display code.
How do I access the convenient existing view helpers from my custom view helper? I.e. in my custom view helper I would like to use Url.Action(), Html.ActionLink, etc. How do I access them from my custom view helper?
using System;
namespace MvcApp.Helpers
{
public class SearchResultHelper
{
public static string Show(Result result)
{
string str = "";
// producing HTML for search result here
// instead of writing
str += String.Format("{1}", result.id, result.title);
// I would like to use Url.Action, Html.ActionLink, etc. How?
return str;
}
}
}
using System.Web.Mvc gives access to HtmlHelpers, but non of the convenient methods like ActionLink seem to be present.

This example should help you. This helper renders different link text depending on whether the user is logged in or not. It demonstrates the use of ActionLink inside my custom helper:
public static string FooterEditLink(this HtmlHelper helper,
System.Security.Principal.IIdentity user, string loginText, string logoutText)
{
if (user.IsAuthenticated)
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, logoutText, "Logout", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
else
return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, loginText, "Login", "Account",
new { returnurl = helper.ViewContext.HttpContext.Request.Url.AbsolutePath }, null);
}
EDIT:
All you would need to do to to access the Url.Action() method would be to replace the this HtmlHelper helper param with something like this UrlHelper urlHelp and then just call urlHelp.Action(...
Hope this helps.

A simple gravatar html helpler, your class needs to be static also.
public static string GetGravatarURL(this HtmlHelper helper, string email, string size, string defaultImagePath)
{
return GetGravatarURL(email, size) + string.Format("&default={0}", defaultImagePath);
}

you can extend the default HtmlHelper and UrlHelper just with an extension method (so you have the xxxHelper as first param in your method).
Or you can just create your base view with the method you want and use the Html or URL variable of the view.

In my opinion, you shouldn't be trying to use ActionLink within code. The whole concept of MVC is to separate logic from display, so you should try to stick with that.
I would suggest you pass the result object through to the view (maybe through ViewData) and then parse the result inline within the view. e.g.
<%= Html.ActionLink(result.title,"/showresult/" + result.id, "myController") %>

Related

How to properly pass string to Razor?

I want to create a simple function in a static class ChardinHtml.DataIntro(string message). The function is supposed to render something like data-intro='my message' and I want to use it that way:
<div #ChardinHtml.DataIntro("These are your site's settings")/>.
(The output would be <div data-intro="These are your site's settings"/> )
What exactly should I return?
Is it string/encoded string/MvcHtmlString/MvcHtmlString with encoded string inside? What should I do to protect myself from characters like ( ' ) (apostrophe) inside a message?
The code looks like this:
public static string DataIntro(string msg)
{
string str = string.Format("data-intro='{0}'", msg);
return str;
}
You can create an HTML helper method to render this. You might return MvcHtmlString as the output. Use HttpUtility.HtmlEncode to encode the string before you use it
public static class MyCustomFancyHtmlExtensions
{
public static MvcHtmlString MyFancyAttr(this HtmlHelper helper, string msg)
{
msg = HttpUtility.HtmlEncode(msg);
return new MvcHtmlString(string.Format("data-intro ='{0}'", msg));
}
}
And you can call it like after including the namespace in the razor view using the using statement
#usint YourNamespaceWhereYouDefinedTheMethod
<div class="test" #Html.MyFancyAttr("test'ing")> test</div>
While this answer your question, I am not sure what your use case is, But my recommendation is to write the data attribute directly in the razor view unless you have some complex logic involved in determining what/when to render this.

How can I return a view(model) and also pass a query string param to the view?

I have an MVC app where users fill in a 4-step form then go to a "confirm" screen. On the confirm screen, if they select to modify their info I use RedirectToAction to take them back to the first step view, and I pass a URL parameter "modify=true", which tells the controller to use the session object already created as opposed to creating a new object from the DB and displaying an empty form. But once they submit the form for step 1 I want to send them from my controller to the step 2 view along with the "modify=true" parameter. There doesn't seem to be a way to return a viewmodel to a view and also pass a query string parameter. How can I accomplish this?
I have considered adding a bool to the viewmodels to signify "inReview" but i use different viewmodels for each of these views and they're all pretty clean, it seems like this bool would muck things up a bit.
I have also considered adding the bool to viewbag or viewdata, but then i'd be using the submit button to pass that value and the "modify=true" parameter would drop off the URL, possibly confusing the user and definitely confusing the code.
Thanks
If you use the Html.BeginForm() helper (without parameters) it will automatically append existing query string parameters to the generated form action attribute. If you use some of the other overloads such as Html.BeginForm("SomeAction", "SomeController", FormMethod.Post) then you're gonna lose those parameters. This could be easily fixed by writing a custom helper that will take into account those parameters:
public static class HtmlHelpers
{
public static IDisposable BeginRequestForm(this HtmlHelper html, string action, string controller, FormMethod method)
{
var builder = new TagBuilder("form");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var routeValues = new RouteValueDictionary();
var query = html.ViewContext.HttpContext.Request.QueryString;
foreach (string key in query)
{
routeValues[key] = query[key];
}
builder.MergeAttribute("action", urlHelper.Action(action, controller, routeValues));
builder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
html.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
return new MvcForm(html.ViewContext);
}
}
and then use in your view (after bringing it into scope of course):
#using (Html.BeginRequestForm("SomeAction", "SomeController", FormMethod.Post))
{
...
}
You can either use ViewBag or your view model. You just need to pass the value somehow to the view:
ViewBag.modify = true;
return View(model);
Then in your view:
Html.BeginForm("MyAction", "MyController", new { modify = ViewBag.modify })

Html ActionLink not posting to action method

I am trying to submit to a controller action method by using Html.ActionLink in my view. I am doing this as non-ajax submit because the return type of my action is FileContentResult (thanks to #Darin for this info). However, my action link is not posting my view to Action Method. Below is my code
View's Code (Partial view)
#Html.ActionLink("Save", "SaveFile", "ui", new { htmlResult="asdf"})
Here, UI is controller name, SaveFile is method name.
Controller method
public FileContentResult SaveFile(string htmlString)
{
...
...
pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(html);
var cd = new ContentDisposition
{
FileName = "MyFile.pdf",
Inline = false
};
Response.AddHeader("Content-Disposition", cd.ToString());
return File(pdfBytes, "application/pdf");
}
When I hit the same URL from browser address bar, then it is hit and also returns the pdf file with no issues. Same this is not happening through action link. I also tried putting the action link inside #using Html.BeginForm().... but no use.
Can you please tell me where I might be doing wrong here?
Thanks!
Html.ActionLink has a lots of overloads and it's very easy to use the wrong one. You are currently using the (String, String, Object, Object) overload which treats your third argument "ui" this route values which leads to a wrongly generated link.
Use this overload instead:
#Html.ActionLink("Save", //Link text
"SaveFile", // Action Name
"ui", // Controller name
new { htmlResult="asdf"}, //Route values
null /* html attributes */)

Add An Anchor To RedirectToAction's Result?

I'm trying to create an extension method similar to MVCContrib's RedirectToAction method by creating a method that will take an #anchor argument and add it to the Url. I am familiar with this question but it's not strongly typed. It's also possible to add values to the query-string, but that won't work for an anchor.
public static RedirectToRouteResult RedirectToAction<T>(this Controller controller, Expression<Action<T>> action, string anchor) where T : Controller
{
var result = new RedirectToRouteResult<T>(action);
// how can I add the #anchor to the result's url?
return result;
}

ASP.NET MVC - HTML Extension method building a URL or link

Consider an extension method whose purpose is to either:
render an <a> tag
on some condition, just return a string without a link
Question: in an extension method, how can you leverage the proper routing logic with Route Values, etc. rather than hardcoding the string. I suspect HtmlHelper.GenerateRouteLink is part of the solution, but please suggest the best way to achieve this.
public static string CreateUserLink(this HtmlHelper html, string userAcctName)
{
if (string.IsNullOrEmpty(userAcctName))
return "--Blank--";
//some lookup to A.D.
DomainUser user = ADLookup.GetUserByAcctName(userAcctName);
if (user == null)
return userAcctName;
//would like to do this correctly!
return string.Format("<a href='/MyAppName/User/View/{0}' title='{2}'>{1}</a>"
, user.Mnemonic, user.DisplayName, user.Location);
//normally returns http://mysite.net/MyAppName/User/View/FOO
}
More info:
using ASP.NET MVC 1.0
I just had to do something similar to this yesterday. There may be a slicker way to do it, but it helps me to see exactly what is going on, so I don't assume anything.
public static string CreateUserLink(this HtmlHelper html, string userAcctName)
{
if (string.IsNullOrEmpty(userAcctName))
return "--Blank--";
//some lookup to A.D.
DomainUser user = ADLookup.GetUserByAcctName(userAcctName);
if (user == null)
return userAcctName;
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues.Add("controller", "User");
routeValues.Add("action", "View");
routeValues.Add("id", user.Mnemonic);
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
TagBuilder linkTag = new TagBuilder("a");
linkTag.MergeAttribute("href", urlHelper.RouteUrl(routeValues));
linkTag.MergeAttribute("title", user.Location);
linkTag.InnerHtml = user.DisplayName;
return linkTag.ToString(TagRenderMode.Normal);
}
would this work?
public static string CreateUserLink(this HtmlHelper html, string userAcctName)
{
if (string.IsNullOrEmpty(userAcctName))
return "--Blank--";
//some lookup to A.D.
DomainUser user = ADLookup.GetUserByAcctName(userAcctName);
if (user == null)
return userAcctName;
return html.ActionLink(user.DisplayName, "user", "View", new {title=user.Location});
//normally returns http://mysite.net/MyAppName/User/View/FOO
}
My experience with GenerateRouteLink has been an uphill battle. It's been a while since I messed with it but if it's the Method I'm thinking of Microsoft has made it "internal" so you can't access and use it outside the MVC assembly. There are a number of workarounds that I played with and didn't really like.
What I ended up doing to avoid hard coding the url in my helper methods is have it accept a 'string url' parameter and use Url.Action in my view when I call the helper method. It's not the cleanest but it's a workaround that worked well for me.
<%= Html.CreateUserLink("userAcctName", Url.Action("Home", "Controller") %>

Resources