Disable links for the current url? - asp.net-mvc

O.K. I think that's an easy one!
I have a ViewMasterPage with some links on it (Home/About/Login/ etc). What I want to achieve is simply have the link disabled when it's referral url is already opened (i.e. if the current url is /Register then the Register link should be disabled .. easy hah!?)
As I don't like to write a lot of in-line coding inside of my views, I end up extending HtmlHelper with some extension methods (just to keep the code in .cs files) and in my views I call these methods, here's my register method as an example:
public static string Register (this HtmlHelper html)
{
TagBuilder builder ;
if (HttpContext.Current.Request.Url.AbsoluteUri.ToUpperInvariant().Contains(MainLinks.Register.ToUpperInvariant()))
return MainLinks.Register; // will return the string "Register"
builder = new TagBuilder("a");
builder.InnerHtml = MainLinks.Register;
builder.AddCssClass("register");
builder.Attributes.Add("href", "/register/");
return builder.ToString();
}
Though this works, it still has two problems:
The hard coded string values of the urls (specially for the home link as I compare the AbslouteUri with "http://www.mysite.com/")
My programming instinct doesn't like it, I feel it should be much simpler than that.
Any ideas!
Ps: No javascipt allowed! It's a javascript-free version of the application.

I don't see too much wrong with this, it's clear to see what it does and it works. However, it's probably better to make it a bit more reusable as I can imagine you repeat yourself a bit with the other links. Maybe something like:
public static string RenderLink(this HtmlHelper html, string text, string url, object htmlAttr) {
if (!HttpContext.Current.Request.Url.AbsolutePath.StartsWith(url, StringComparison.InvariantCultureIgnoreCase)) {
return text; //comparison excludes the domain
}
TagBuilder tag = new TagBuilder("a");
tag.SetInnerText(text);
tag.Attributes.Add("href", url);
//... add attributes parsed as htmlAttr here
return tag.ToString();
}
Then add your links to your view like:
<%= Html.RenderLink("Register", "/register/", new { #class="register"}) %>
<%= Html.RenderLink("Account", "/account/", new { #class="account"}) %>
If you wanted to get away from the hard coded domain, then using Request.Url.AbsolutePath instead of AbsoluteUri as above achieves that.
The alternative would be to parse the current page information in the model from the controller, maybe like ViewData.Model.CurrentPage = "Register";, but I wouldn't advise you doing that as I don't see it being the job of the controller in this case.

Related

ASP.NET Razor C# Html.ActionLink to create empty link

How would you use an Html.ActionLink to render the following link -
It may seem silly to do this, but sometimes I need a link that has link functionality (rollover pointers, etc.) but does not go anywhere. And I want to use an Html.ActionLink for code consistency.
I have tried different variations of Html.ActionLink but I keep getting messages about things not allowed to be null.
#Html.ActionLink(" ", "", "", new {href="javascript:void(0)"})
will render as
Instead of forcing ActionLink to do something it isn't made for, consider creating your own helper method:
public static class MyHtmlExtensions
{
public static MvcHtmlString EmptyLink(this HtmlHelper helper, string linkText)
{
var tag = new TagBuilder("a");
tag.MergeAttribute("href", "javascript:void(0);");
tag.SetInnerText(linkText);
return MvcHtmlString.Create(tag.ToString());
}
}
Import the namespace into your view and you'll be able to do this:
#Html.EmptyLink("My link text")

In ASP.Net MVC, how can you manage dependencies between views and layout pages?

We're working with ASP.Net MVC and Google Publisher Tags (GPT).
GPT requires that you create javascript "slots" in the <head> and some html+script that goes into the <body>. The key dependency here is that in both places is an id that must match. So the head will contain some Javascript that includes something like:
<head>
<script>
...
DefineSlot('div-gpt-ad-123456789-0', 'foo', 'bar')
...
</script></head>
and
<body>
...
<div id='div-gpt-ad-123456789-0'>
<script>
...
Display('div-gpt-ad-123456789-0')
...
</script>
</div>
...
How can we manage the dependencies between these 2 pieces code? The critical piece is that the id of both parts must match.
We want to use MVC to create these pieces of code dynamically. So in any view, partial or layout, I will be able to add a helper call that might look like:
#Html.CreateAd(size, "foo", "bar")
#Html.CreateAd can be called anywhere in a view, partial view, layout, or nested layout.
How do you use ASP.Net MVC to program the code that goes into <head>?
Any suggestions are appreciated. I'm just looking for direction, not a full blown solution.
Many Thanks.
You have a few different ways to do this.
You can add the id's to the ViewData or a base viewmodel.
Then OnActionExecuting or OnActionExecuted in a base controller or via actionfilters, you can add your data to whichever place you prefer to. If you need examples for this, please leave a comment on this answer.
Then, your helpers (one for each section) you can read from one of the 2 sources you decided on. I have gone both routes. If all of your pages are going to have an ad, then I would lean towards the base ViewModel. If it's more of a rare occurrence ViewData would be more appropriate.
To access the viewdata within an htmlHelper Extension method:
public static class HtmlExtension
{
public static MvcHtmlString RenderAdHead(this HtmlHelper h)
{
h.ViewContext.ViewData.Model // a test and cast here
h.ViewContext.ViewData["AdIdentifier"] // test for null and cast here
string tags = String.Empty;
//build up string to resemble your script/html tags using either of the
//sources above, so long as either source is not empty.
return new HtmlMvcString(tags);
}
}
And some code for a filter:
public class AdvertisingFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<String> idList = null; // list of string might not be adequate. depends on your implementation
//read advertising ids from your datastore or wherever you have.
filterContext.Controller.ViewData["advertisingFilter"] = idList;
}
}
The basecontroller is pretty much the same, instead you have the controllercontext directly. You just have to be sure all your controllers inherit from them.

Is there simple way to use bookmarks in controller action's generated urls?

Currently I am working with simple Forum module in ASP.NET MVC 3 which i will add later to my main application. The possibility to link to certain blocks like div used to present thread replies is very useful.
I figured out something which work and propably will be enough for my needs, I just wonder if there is something more elegant and simple aswell. I found a solution using ActionFilters, and since I'm quite begginner in MVC I would like to find some easier solution (if exists). Well i will propably learn ActionFilters soon aswell :)
So here is what I've done:
public ActionResult ShowThread(int id, int? postID)
{
var thread = db.ForumThreads.Find(id);
if (postID != null)
{
return Redirect(Url.Action("ShowThread",new {id=id})+"#post-"+postID.ToString());
}
return View(thread);
}
I know it is quite simple, but it is working. Also it doesn't check if the postID is valid yet, but it is not a part of question.
The solution is to use one of RedirectToAction overloads: http://msdn.microsoft.com/en-us/library/dd470154(v=vs.108).aspx.
Perhaps, in your case, the next one would do: http://msdn.microsoft.com/en-us/library/dd460291(v=vs.108).aspx
And the call would be:
return this.RedirectToAction("ShowThread", new { id = id, post = postID });
Another (simpler, but not safer!) solution is to format the URL you need to redirect to and to use the Redirect() method like this:
var redirectUrl = string.Format("/Home/ShowThread/{0}?post={1}", id, postID);
return this.Redirect(redirectUrl);
Pay attention to your URL mappings, though. The examples above assume the method
public ActionResult ShowThread(int id, int? post)
is in HomeController and the default route has not been changed. Otherwise you should
adjust the URL prepending the controller name (w/o Controller), or
change your default route to map to the controller's name.

create a textarea helper that takes content of the view as a parameter

I would like to create a html helper that would receive as a parameter content of the view, like this:
<% Html.MyTextArea("txt1", () => {%>
content of the view
<%=Html.TextBox("Name") %>
...
<% }) %>
Since you've tagged it as MVC, I'm going to propose you could do something like I posted on my blog as a way to get syntax highlighting for templates as the solution would be very similar, IF you don't need to manipulate the inner content and are simply interested in 'wrapping' it in some way (like in a containing element that requires some extra logic).
Using the technique, the HtmlHelper method receives the context of the block. The syntax is slightly different from your suggested technique though.
For example, you could have something like:
#using(Html.MyTextArea("txt1"))
{
<some HTML content here>
}
The context is passed to an IDisposable object which includes a Writer (for writing to the current output stream). There, it can output multiple elements or do other manipulation as needed. The Dispose is used to write a close element (if needed).
So, you could end up with something like this:
<textarea><some HTML content here></textarea>
However, as I mentioned this does not provide the inner content to the function itself.
As Razor pages render inside out, there's not an effective method to grab the output in the way you're wanting. There are some posts around about caching the output of a Partial to a string (which would mean the inner content in your example would be in another file, an .ascx file), so you might want to look at those.
One approach is,
public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, Action<TextWriter> action)
{
var writer = new StringWriter();
action(writer);
// here writer contains the html
return htmlHelper.TextArea(name);
}
<%:Html.TextArea("txt1",(writer) => {
writer.Write("content of the view");
writer.Write(HttpUtility.HtmlEncode(Html.TextBox("Name")));
}) %>
Do you mean something like this?
namespace System.Web.Mvc {
public static class HtmlHelperExtensions {
public static MvcHtmlString MyTextArea(this HtmlHelper htmlHelper, string id, Func<MvcHtmlString> helperFunc) {
return new MvcHtmlString(string.Format("<div id=\"{0}\">{1}</div>", id, helperFunc()));
}
}
}
You can use this way:
<%: Html.MyTextArea("txt1", () => Html.TextBox("Name", "abc")) %>

Proper procedure for tag helpers in moustache

I've been reading over the moustache documentation, and it looks cool:
https://github.com/defunkt/mustache
There is a clear separation between ruby code and html markup. Having not started a moustache project, my confusion is this: how should ruby that generates markup be handled? Obvious examples would be form helpers and link_to, not to mention any custom methods. Would it be proper (or possible) to do {{link_to ...}}? -- having code in the markup. Or to define a custom method in the view ruby: {{whatever_link}}? -- this seems non-DRY and equally undesirable. Where's the win?
Thanks!
--Peter
Mustache is very simple by design. Templates are supposed to be renderable in many languages (see https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations). Many web applications chose mustache because templates can be shared by client code (javascript) and server code (whatever language).
The win is not to go against that fact, even if it may look inconvenient at first sight.
Investigate the {{whatever_link}} solution, and keep your code as DRY as you can.
Here is what I do in mustache.java:
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class UrlHelper implements Map<String, String> {
public static final UrlHelper instance = new UrlHelper();
private static final String NORMAL_PREFIX = "url";
private final String virtualHost;
// configure virtualhost here
private UrlHelper() {
virtualHost = "/";
}
public boolean containsKey(Object key) {
return key.toString().indexOf(':') != -1;
}
public String get(Object key) {
String k = key.toString();
String prefix = k.substring(0, k.indexOf(':'));
String url = k.substring(k.indexOf(":") + 1);
if (prefix.equals(NORMAL_PREFIX))
return virtualHost + url;
return virtualHost + "static/" + prefix + "/" + url + "." + prefix;
}
// Default implementation for the other 'Map' methods
}
When rendering I do this:
mustache.execute(writer,new Object[] { model, UrlHelper.instance }).flush();
You can use like this(assume your app is hosted at /app):
{{js:lib/jquery}} {{! /app/static/js/lib/jquery.js }}
{{css:site}} {{! /app/static/css/site.css }}
{{url:users/edit/1}} {{! /app/users/edit/1 }}
It is also possible to have a single prefix for all static resources(eg: static), but you can't use dots to specify the file extension, so you must use some other character to represent dots and replace in your processing code.
You can probably adapt this technique to your web framework/language.
I agree with Gwendal that a goal of Mustache is to be able to share views between your backend and frontend, though that is not the only goal -- you might use Mustache if you just want to clean up things in your views. In that light, another way to think about Mustache is that it pushes the logic to the layer behind the immediate frontend (i.e. Ruby code). So in this middle layer you can do anything you want. I would recommend using the stache gem because it fills in the gap between Mustache-land and Rails-land by making a connection between your Mustache view class and the underlying Rails view class. So this way, you could use, for instance, link_to in this middle layer. You'd have to make a view method that wrapped a call to link_to for a particular object, since you can't call methods in a Mustache template and pass it arguments, but I think that is okay.

Resources