Archive

Archive for the ‘MVC’ Category

ASP.Net MVC Tip: URL Anchor Tags

Tuesday, 6 December 2011 7:11 pm Comments off

This is one of those features which should be baked into MVC by default, but isn’t.

Actually, it is partially supported by MVC. This Codeplex answer details it:

 

@Html.ActionLink(
    "Link Text",           // linkText
    "Action",              // actionName
    "Controller",          // controllerName
    null,                  // protocol
    null,                  // hostName
    "fragment",            // fragment
    new { id = "123" },    // routeValues
    null                   // htmlAttributes
)

will produce (assuming default routes):

<a href="/Controller/Action/123#fragment">Link Text</a>

But what if you want the returned redirect of an ActionResult method to include the anchor tag. There is no RedirectAnchorTagResult to return.

 

That same answer does some voodoo to pull it off:

public ActionResult Index()
{
    var url = UrlHelper.GenerateUrl(
        null,
        "Action",
        "Controller",
        null,
        null,
        "fragment",
        new RouteValueDictionary(new { id = "123" }),
        Url.RouteCollection,
        Url.RequestContext,
        false
    );
    return Redirect(url);
}

 

It is this that should be baked into MVC by default. Thats an awful lot of complexity to expose just for the sake of a simple anchor tag. So, yes, a RedirectAnchorTagResult would be a nice addition.

 

Thanks to Darin Dimitrov for providing the answer. Go ahead and upvote his answer.

Categories: Codeplex, MVC, Programming, Tech

LINQ + Google Charts + MVC : Pie Chart

Tuesday, 1 November 2011 3:52 pm 1 comment

A couple of weeks ago, I pointed out a LINQ snippet that was running against the Google Charts API.

This week, I’m back with my implementation of  the Pie Chart.

I’ve started with the Pie chart because its relatively simple and will lay the foundation for dealing with the complexities of the other charts.

Building Blocks

Since this is MVC, I created a new ChartsService class in the Services namespace of the project.

If you look at the snippets in the original ACM article, you’ll notice an extension method called “SeparatedBy”. So our first task is to create this extension method.

There are, of course, a number of ways to concatenate a list of string with a separator. This being an exercise in LINQ, we are going to use the LINQ Aggregate method.

   
public static string SeparatedBy(this List<String> list, string seperator)
{ 
return list.Aggregate((current, next) => current + seperator + next); 
}

 

I’m sure that you’ll agree with me when i say that it’s a nice and clean approach to what could be a messy block of code.

However, that extension method will only concatenate the list of strings passed to it. Why is this a problem? Because we are going to want to concatenate lists of int objects as well. So that extension method will just not do. We  could do some fancy work with generics, but the simplest thing to do is to supply to an overloaded method that accepts lists of type int.

public static string SeparatedBy(this List<int> list, string seperator)

List<string> thelist = list.ConvertAll<string>(x => x.ToString());

 return thelist.Aggregate((current, next) => current + seperator + next); 
}

There is one additional difference between these and our original method – the call to ConvertAll. Rather than have a foreach loop that does the conversation, we simply supply an inline lambda function that gets executed against each item  in the list and returns a list of the desired type. Again, very clean.

 

So, armed with these extension methods, we can now declare our classes.

Data

Google charts offers a wide range of functionality and many different kinds of charts. Each chart has a host of differing options and settings available to it. So when creating classes to represting thse charts kinds we have to bear in mind that there will be unique functionality not common to other charts that will come up.

Charts logically are made up of a number of  smaller complements:  bar charts have columns, pie charts have slices and so on and so forth. So we’ll represent these first.


public class Slice
{
public int Value { get; private set; }
public string Legend { get; private set; }

public Slice(int value, string legend)
 this.Value = value;
this.Legend = legend;
}

} 

Lets first look at the Pie class itself now.

Pie Class


public List<Slice> Slices { get; set; }
public string Title { get; private set; 
public Double Height { get; private set; }
public Double Width { get; private set; }

public Pie(string title, double height, double width)
{
this.Slices = new List<Slice>();
this.Title = title;
this.Height = height;
this.Width= width;
}

We start by declaring a number of properties and a constructor. In the constructor we initialize our list of Slices. This is where we see a departure from the snippets of he ACM article.  We do not pass the slices into the constructor. Of course, this is an issue of style over substance. There is no reason why we could not have generated the slices before the creating the chart and then passed the slices.


public string Compile()
{
var tt = Title.Split(' ').ToList().SeparatedBy(' ');
var p = new List<string>(){
this.Slices.Select(slice =>slice.Legend).ToList().SeparatedBy("|"),
this.Slices.Select(slice =>slice.Value).ToList().SeparatedBy(",")};

return string.Format(@"http://chart.googleapis.com/chart?cht=p&chtt={0}&chs={3}x{4}&chl={1}&chd=t:{2}", tt, p.ElementAt(0), p.ElementAt(1), this.Width, this.Height)
}

This is where all the important stuff happens.

Line 3 properly formats the title by putting a + sign to signify spaces between words. Note that we are using the extension method we wrote earlier.

Line 4 creates a new list of strings, each string being the comma or | delimited list of values and legends. Using a List gives us greater flexibility later on when our implementation will handle multiple data series and legends

Line 8 uses String.Format to merge all this data into a url that we can use to call the Google Charts API with. For an explanation of what each of these parameters mean, see the Google Charts API Parameter List

 

ViewModel

Now, this being MVC, the way to display stuff is by using a ViewModel. So lets create one:


public class MonthlyReportsViewModel
{
public MonthlyReports Details { get; set; }
public Pie Chart { get; set; }
 }

The one property we are interested in here is the Chart Property.

Now that we have our ViewModel, we have to populate it. So, in our ActionResult method:


<pre>Pie chart = new Pie("This is my chart",200);
chart.Slices.Add(new Slice(25, “Slice 1”));
chart.Slices.Add(new Slice(25, “Slice 2”));
chart.Slices.Add(new Slice(25, “Slice 3”));
chart.Slices.Add(new Slice(25, “Slice 4”));
model.Chart = chart;
return View(model);

 

View

In our View itself, we’re going to have to render out the chart. Since the call to Google Charts API will return an image, we can simply do the following:


<img src = "@Model.Chart.Compile()" alt ="">

 

What one could do is to put the actual rendering code in a Helper Method and call the Helper Method from your view, like so:


@GoogleCharts.PieChart(@Model.Chart)

That, of course, further abstracts the code. It does have the advantage of being much cleaner and easier to do.

 

Conclusion

As you can see, using LINQ to abstract away the complexity of what your code is actually doing is not just the province of database code. One thing what I’ve enjoyed about working with LINQ is how code always comes out looking fresh, clean and crisp. Having worked extensively with LINQ and Lambda expressions, using foreach loops  to process Lists looks so much messier.

Next time, we’ll take a look at the somewhat more complicated Bar Chart. I’ll not cover every single possible piece of functionality, but I’ll cover the basics. All I want to show is how the foundation laid down today can easily translate over. My current implementation of bar charts is sufficient only for the limited functionality the app needs and nothing more. 

At some point in the future, I’d also like to implement Line charts.

 

Postscript

I must say that apart from working with LINQ, its been a very satisfying experience for me to implement a C# version of a web API.

There is GoogleChartsSharp on Codeplex that Implements a whole lot more of the functionality of Google Charts. I did indeed use it for a while before implementing it on my own.

So its been a satisfying experience for me to implement an API that allows me to work the way I want to work. Not only did I write something simpler and easier to work with, but I dropped a dependency in the process and that made me happier than I think its safe to admit. 

Writing against something requires you, the writer, to pay extra attention to the small details. It requires you to think of the relationship between your code,the web API  calls and the documentation that supports it. When one uses an already baked implementation such as GoogleChartSharp, its like working with a giant black box  you have no idea what goes on inside. And you really don’t want to know the finer details. But writing the API, you create a white box. And you HAVE to understand those finer details.

So while the LINQ is nothing special in and of itself,nothing earth shattering or ground-breaking, it is the experience and the satisfaction gained from it that makes this a worthwhile post to write.

Categories: Google, LINQ, MVC, Programming, Projects, Tech, Web

From the Department of MVC useful goodies (and sponsored by the department of Stackoverflow saved my butt)

Saturday, 7 May 2011 7:56 pm Comments off

I use the ipinfodb.com API in my app a lot to do geolocation. Well, as such things are wont to do, it went down for about an hour today. Curiously, the production site wasn’t affected at all, but my dev work was. So, in panic mode  I needed to add a country selector so that the rest of the site would have  the country information it needed. Now the reason there wasn’t one already was a deliberate design choice. So, i needed a backup plan for the next time ipinfodb went down.

As usual, Stackoverflow saved my butt (again). There’s a great answer that explains the way to do things in MVC using Razor.

Rather than steal the guys thunder, I’m just going to add one recommendation. In the body of the javascript function add the following:

location.reload();

And the page refreshes, including any changes triggered by the selection.

Go on and read the answer here.

Categories: MVC, Programming, Tech, Web

Sitemaps in ASP.Net MVC: Icing on the Cake

Friday, 8 April 2011 4:23 pm 6 comments

This is short simple and sweet (forgive the pun).  The reason why i say that is that you have two options when doing a sitemap in MVC (actually, you have more, but whatever).

The first is using a Library. There’s a MVC Sitemap provider on Codeplex that you can download and install. It involves some XML configuration and attributes on all the actions you want to include in your sitemap.

The fact is, I don’t have time to fiddle around with configurations. I just want a simple sitemap file with a handful of products, categories and one or two other links. If the site was larger and more complex I might consider it.

So, we come to the second, DIY way. Now, this is not entirely my idea. I just repurposed it to pull the correct URL parameters out of the db. The original code is found on Codeplex.

Firstly, we have to register a new route to www.example.com/sitemap.xml. Go to Global.asax and put the following in your RegisterRoutes() method. I put mine after the call to IgnoreRoute.

routes.MapRoute("Sitemap", "sitemap.xml", new { controller = "Home", action = "Sitemap", id = UrlParameter.Optional });

Now, you can use any default routing you want with this. As you can see above, the route is pointing to the Sitemap action of the Home controller.

Then we have to actually populate our Action with some code.

  protected string GetUrl(object routeValues)
        {
            RouteValueDictionary values = new RouteValueDictionary(routeValues);
            RequestContext context = new RequestContext(HttpContext, RouteData);

            string url = RouteTable.Routes.GetVirtualPath(context, values).VirtualPath;

            return new Uri(Request.Url, url).AbsoluteUri;
        }
        [OutputCache (Duration=3600)]
        public ContentResult Sitemap()
        {
            var categories = storeDB.Categories.Include("Products").Where(g => g.Id != 8); //some filtering of categories
            XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
            XElement root = new XElement(xmlns + "urlset");

            List<string> urlList = new List<string>();
            urlList.Add(GetUrl(new { controller = "Home", action = "Index" }));
            urlList.Add(GetUrl(new { controller = "Home", action = "Terms" }));
            urlList.Add(GetUrl(new { controller = "Home", action = "ShippingFAQ" }));
            urlList.Add(GetUrl(new { controller = "Home", action = "Testimonials" }));
            foreach (var item in categories)
            {
                urlList.Add(string.Format("{0}?{1}={2}",GetUrl(new { controller = "Store", action = "BrowseProducts"}),"category",item.Name));

                foreach (var product in item.Products)
                {
                    urlList.Add(string.Format("{0}/{1}", GetUrl(new { controller = "Store", action = "ProductDetails" }), product.Id));
                }
            }

            foreach (var item in urlList)
            {
                root.Add(
                new XElement("url", 
                new XElement("loc", item), 
                new XElement("changefreq", "daily")));
            }

            using (MemoryStream ms = new MemoryStream())
            {
                using (StreamWriter writer = new StreamWriter(ms, Encoding.UTF8))
                {
                    root.Save(writer);
                }

                return Content(Encoding.UTF8.GetString(ms.ToArray()), "text/xml", Encoding.UTF8);
            }
        }

Essentially, we’re just outputting an xml file with the correct format and structure.  This gives us a file that looks like:

<?xml version="1.0" encoding="utf-8"?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

  <url xmlns="">

    <loc>http://localhost:26641/</loc>

    <changefreq>daily</changefreq>

  </url>

  <url xmlns="">

    <loc>http://localhost:26641/Home/Terms</loc>

    <changefreq>daily</changefreq>

  </url>

  <url xmlns="">

    <loc>http://localhost:26641/Home/ShippingFAQ</loc>

    <changefreq>daily</changefreq>

  </url>

You get the idea.

The above code is using Entity Framework 4.1, so you can replace the line that declares  “var categories” with whatever data source you have. And you’ll have to reformat the url strings to conform to your parameter format.

Now I’m not suggesting this for any large MVC deployment. The code could get rather messy.

 

But for something simple, it works like a dream.

Using MVC and jQuery to build a NewsTicker

Sunday, 5 December 2010 12:44 am 6 comments

Scott Guthrie tweeted a link to an article by Scott Mitchell. In it Scott wrote a handy news ticker in Asp.Net . And he wrote it in VB. I recommend going to read it before doing anything else.

It is a handy little example and a very good demonstration of jQuery in action. I actually first thought of using it in The Feedreader. Of course if I wanted to do that I’d have to re-write it as an MVC application rather than an ASP.Net website.

This is a nice academic exercise on moving from ASP.Met to MVC.

So lets get cracking.

Groundwork

So. We begin by creating an empty MVC2 project.

Actually, we began when we read Scott article. Its important to understand what Scotts trying toacomplish and how Scotts code works, before we shamelessly copy it.

The first thing you are going to want to do is make sure that Scotts’ ticker.js and jquery-1.4.4.min.js are in your MVC Scripts folder and included in the project. Also, you’ll want to copy Scott’s css files across. I put them in a folder called Styles and made sure it was included in the project.

Now, we need a Masterpage before we can create any Views. So add one in Views/Shared. We are shamelessly copying Scotts example in every detail. So go ahead and copy the markup in Scotts example and paste it into the your masterpage. You’ll want to change the script and css paths accordingly.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
<script type="text/javascript" src="../../Scripts/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="../../Scripts/ticker.js"></script>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
<link href="../../Styles/sinorcaish-screen.css" rel="stylesheet" type="text/css" />
<link href="../../Styles/CustomStyles.css" rel="stylesheet" type="text/css" />
<link href="../../Styles/NewsTicker.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<!-- ======== Header ======== -->
<div id="header">
<div class="left">
News Ticker Demo
</div>
<div class="right">
<%=DateTime.Now.ToShortDateString()%>
</div>
<div class="subheader">
<em>News me!</em>
</div>
</div>

<!-- ======== Left Sidebar ======== -->
<div id="sidebar">
<div>
<ul>
<li><%: Html.ActionLink("Simple Ticker Demo", "Simple","Home")%></li>
<li><%: Html.ActionLink("Dynamic Ticker Demo", "Dynamic","Home")%></li>
</ul>
</div>
</div>

<!-- ======== Main Content ======== -->
<div id="main">
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">

</asp:ContentPlaceHolder>
</div>

<!-- ======== Footer ======== -->

<div id="footer">
ASP.NET application designed by <a href="http://www.4guysfromrolla.com/ScottMitchell.shtml">Scott Mitchell</a>.
Website design by <a href="mailto:J.Zaitseff@zap.org.au">John Zaitseff</a>, and available
at <a href="http://www.opendesigns.org/preview/?template=1700">OpenDesigns.org</a>.
</div>

</form>
</body>
</html>

Remember to change the links in the sidebar to ActionLinks.

Controllers

The next thing we need to do is to write a Controller. Typically the first controller in any MVC application is the home controller.

So go ahead and create one, adding three ActionResult methods: Index, Simple and Dynamic.

public class HomeController : Controller
{
//
// GET: /Home/

public ActionResult Index()
{
return View();
}
public ActionResult Simple()
{
return View();
}
public ActionResult Dynamic()
{
return View();
}
}

At this point, create a View for Index and copy the HTML from Default.aspx and stick it in the content control thats been created in the view.
Then, add an empty view for the Simple controller. Since this is straightforward HTML, we simply copy the contents of both ContentPlaceHolders in Simple.aspx into the two that have been created for us in the view

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

<h2>Simple News Ticker Demo</h2>
<p>
This demo shows two simple news tickers. Each news ticker has the same hard-coded news items. The
first one shows only the body of each news item, one at a time; the second one shows the headline,
body, and published date of each news item and shows three at a time.
</p>

<h3>One Row News Ticker</h3>
<div class="ticker stretched">
<ul id="latestNews1">
<li>
<div class="body">Politician Joe Smith has assembled a news conference for this afternoon to apologize for some
indiscretion he had. This is Mr. Smith's third such "apology press conference" this year.</div>
</li>
<li>
<div class="body">Did you know that you can play the fun (and addictive!) board game
Axis &amp; Allies online? Head on over to <a target="_blank" href="http://gamesbyemail.com/Games/WW2">http://gamesbyemail.com/Games/WW2</a>
and give it a whirl!</div>
</li>
<li>
<div class="body">A recent study by some doctors somewhere showed a strong correlation between unhealthy eating
and unheathly people. More studies are to be performed to verify these findings.</div>
</li>
<li>
<div class="body">This just in - ASP.NET is awesome! jQuery is not so bad, either. In fact, most technologies are pretty darn
cool. For more information, see <a href="http://www.4guysfromrolla.com/" target="_blank">4GuysFromRolla.com</a>.</div>
</li>
<li>
<div class="body">Last night the local sports team won a convincing victory over their hated rivals. After the game there was much
jubilation.</div>
</li>
<li>
<div class="body">Visit my blog. Please. You can find it at <a href="http://scottonwriting.net/sowblog/">ScottOnWriting.NET</a>.</div>
</li>
</ul>
</div>

<h3>Three Rows News Ticker</h3>
<div class="ticker threeRows medium">
<ul id="latestNews3">
<li>
<div class="header">Politician schedules news conference</div>
<div class="body">Politician Joe Smith has assembled a news conference for this afternoon to apologize for some
indiscretion he had. This is Mr. Smith's third such "apology press conference" this year.</div>
<div class="footer">Published @ 8:30 AM</div>
</li>
<li>
<div class="header">Play Axis &amp; Allies Online!</div>
<div class="body">Did you know that you can play the fun (and addictive!) board game
Axis &amp; Allies online? Head on over to <a target="_blank" href="http://gamesbyemail.com/Games/WW2">http://gamesbyemail.com/Games/WW2</a>
and give it a whirl!</div>
<div class="footer">Published @ 8:38 AM</div>
</li>
<li>
<div class="header">Study links unhealthy food to unhealthy people</div>
<div class="body">A recent study by some doctors somewhere showed a strong correlation between unhealthy eating
and unheathy people. More studies are to be performed to verify these findings.</div>
<div class="footer">Published @ 9:00 AM</div>
</li>
<li>
<div class="header">ASP.NET is awesome!</div>
<div class="body">This just in - ASP.NET is awesome! jQuery is not so bad, either. In fact, most technologies are pretty darn
cool. For more information, see <a href="http://www.4guysfromrolla.com/" target="_blank">4GuysFromRolla.com</a>.</div>
<div class="footer">Published @ 9:09 AM</div>
</li>
<li>
<div class="header">Local sports team wins</div>
<div class="body">Last night the local sports team won a convincing victory over their hated rivals. After the game there was much
jubilation.</div>
<div class="footer">Published @ 9:35 AM</div>
</li>
<li>
<div class="header">Read my blog</div>
<div class="body">Please. You can find it at <a href="http://scottonwriting.net/sowblog/">ScottOnWriting.NET</a>.</div>
<div class="footer">Published @ 10:30 AM</div>
</li>
</ul>
</div>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
<script type="text/javascript">
$(document).ready(function () {
startTicker('#latestNews1', 1, 5000);
startTicker('#latestNews3', 3, 5000);
});
</script>
</asp:Content>

Note the javascript in the “head” ContentPlaceHolder. This fires as soon as the page as finished rendering. Scott has more details about it in his article.

Model

Now, this is the hard part.

Scott Mitchells’ example used an ASP ListView control. If you’re writing ASP.Net code a listview is the easiest way to accomplish what we’re trying to do.  You’ll notice as well that Scott passes the contents of SyndicationFeed.Items directly to the databound control. Now there is most probably a way of using Scott’s code directly in an MVC view. However, for the purposes of convenience we’ll dispense with the ListView and iterate over items ourselves.

There is also the issue of the Formatting of the items. You’ll notice that the ItemTemplate in Scotts’ code calls FormatSummary and FormatPubDate from the HTML. Because of the separation between code and HTML in MVC we can’t do that.

The solution to both of these “problems” is to do things ourselves. The M in MVC stands for model. So we need a model before we go any further. The two pieces of data we need are contained in the Summary and the PublishDate fields of the SyndicationItems. So this is what our model looks like:

public class Model
{
public String Title { get; set; }
public string Date { get; set; }
}

FormatSummary and Format PubDate, obviously need to be part of  the HomeController class:

public static string FormatSummary(string summary){
string header = "ScottOnWriting: ";

//Remove the leading "ScottOnWriting: "
if (summary.StartsWith(header)){
return summary.Substring(header.Length);
}
return summary;
}

public static string FormatPubDate(DateTimeOffset pubDate)
{
return pubDate.ToString("h:mm, MMM d");
}

View

Now that the groundwork has been laid, we can write our actual HTML view.  First we need to add our javascript function:

<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
<script type="text/javascript">
$(document).ready(function () {
startTicker('#tweets', 2, 4500);
});
</script>
</asp:Content>

This javascript function will do nothing unless ticker.js exists in your scripts folder. It will end up in the page header and will be executed as soon as the document has finished rendering. Also note that we are passing 2 in here. We could pass in any number we wanted. Scott explains more about this in his article.

Now, the fact is that the original implementation using a ListView basically iterated over all the objects in the datasource and output a select piece of HTML for each item in that collection. So, we’ll do the same.

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

<h2>Dynamic News Ticker Demo</h2>
<p>
This demo shows a ticker whose contents are populated dynamically. This particular example pulls the most recent tweets from
<a href="http://twitter.com/ScottOnWriting">my Twitter account</a> and displays them in a ticker, showing two entries at a time.
</p>
<h3>@ScottOnWriting's Latest Tweets</h3>
<div class="ticker twoRows medium">
<ul id="tweets">
<%foreach (var item in this.Model)
{  %>
<li>
<div class="header" style="font-weight: normal">
<%= item.Title.ToString()%>
</div>
<div class="footer">Tweeted @ <%: item.Date.ToString()%></div>
</li>
<%} %>
</ul>
</div>
</asp:Content>

Note the careful placement of our Foreach  statement. Our list item (<li/>) and the HTML with in it will be repeated on each pass over the loop body. Also, note how we are using var item in this.Model. Our View knows exactly what datatype has been passed to it. In fact, our view is actually View<Model.Model>. Yes, its a generic. And we are using var here to avoid typing Model.Model over and over again.

 

So, in a nutshell, the above code does the exact same thing as a ListView Control.

Hit run and it should be working.

Follow

Get every new post delivered to your Inbox.