Javascript Corner - jquery.lazyload

I've used this usefully little plugin on several projects, and Scott Hansleman recently highlighted it in his Pinching pennies when scaling in The Cloud blog post. It's useful for situations where:

  • You want to load images based on some convention but aren't sure if they exist
  • You want to load only images that the use is able to see on their page

Take this example that I've been working on this week. It's a leader board for our timesheeting application that shows the current top biller for the year

In this instance, the employee profile images are being loaded by convention, and not all employees have photos, that's why you see broken images here. Now there's a few ways of solving this:

  1. Check the file system for the employee file (this has a few problems)
  2. Check via HTTP the image exists on the external server

The problem with checking the file system is, that the file can still be broken if it is trying to get an image served from a CDN instead, 

Checking via HTTP headers is possible and can be done without downloading the file but as always, if you have lots of files this can slow down the rending of your page.

jQuery.LazyLoad is a great client side solution as we first render a placeholder image and then (on the client side) request the actual image and then replace the placeholder with the downloaded image. It's very lightweight and simple to use.

    • Reference the jquery.lazyload.min.js file

    <script src="@Url.Content("~/Scripts/jquery.lazyload.min.js")"></script<
    
    • Render the image tags as follows:
     <img class="full lazy" data-original="@emp.PhotoUrl" src="@Url.Content("~/Content/EmpPhotos/placeholder.png")" />
    
    • Call the lazyload() method on document ready
       <script>
            $(function() {
                $("img.lazy").lazyload();
            });
        </script>
    

    Now all the broken images have a place holder image by default, and the client downloads and replaces the employee photos for employees who actually do have a photo.

    Javascript Corner - jquery.cookie

    I'm trying to do a regular post on useful javascript libraries and plugins to existing frameworks such as jQuery, knockoutJs etc...

    So to kick things off, let me introduce jquery.cookie. This usefully little library allows you to get and set cookies on the client side. Now why might you want to do this?

    • Serve up a generic cached page and personalize based on cookie information (a lot of sites do this, like eBay shows your name, but will still require you to sign in)
    • Retrieve some information via AJAX and save it to a cookie

    I've used this on several projects in the past, and it's nice and easy to use.

      <script src="/path/pto/jquery.cookie.js"></script>
      
      • .Set a cookie
      var userProfile = {
          Name: 'Eric Phan',
          Age: 28,
          LastLogin: '2013-04-22 11:24 PM' 
      };
      
      var cookieOptions = {
          expires: 7, // expire in 7 days	
          path: '/', // path of the cookie
          domain: 'ericphan.net', // domain the cookie is valid for
          secure: false // whether to transmit the cookie over HTTPS
      };
      
      // Need to store the JSON data as string
      $.cookie('UserProfile', JSON.stringify(userProfile), cookieOptions);
      
      • Get a cookie
      var cookie = $.cookie('UserProfile');
      
      // Convert the string value back to JSON
      var userProfileData = JSON.parse(cookie);
      
      console.log(userProfileData.Name)
      
      • Delete a cookie
      $.removeCookie('UserProfile', { path: '/', domain: 'ericphan.info'});
      

      Really simple and easy to use. Props to Klaus Hartl for developing such a handy little plugin.

      Switching to Javascript and CSS Bundling in MVC 4

      I was working an an internal project that started on MVC 2, and has recently been upgraded to MVC 4. There's a lot of house keeping to do to keep the codebase maintainable.

      One custom piece of code that was implemented to make sure assets like javascript and css were not cached when a new version was released was implemented as follows:

      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/themes/base/jquery-ui.css" + Versioning.Latest)" />        
      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/kendo/2012.2.913/kendo.common.min.css" + Versioning.Latest)" />
      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/kendo/2012.2.913/kendo.metro.min.css" + Versioning.Latest)" />
      <link rel="stylesheet" type="text/css" href="@Url.Content("~/Content/Site.css" + Versioning.Latest)" />    
      <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/jquery.min.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/kendo.all.min.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/kendo/2012.2.913/kendo.aspnetmvc.min.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-ui-1.9.2.min.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.formatCurrency-1.4.0.min.js" + Versioning.Latest)"></script>   
      <script type="text/javascript" src="@Url.Content("~/Scripts/date.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.watermark.min.js" + Versioning.Latest)"></script>   
      <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.cross-slide.min.js" + Versioning.Latest)"></script>    
      <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.cookie.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/browser.support.js" + Versioning.Latest)"></script>
      <script type="text/javascript" src="@Url.Content("~/Scripts/handlebars.min.js" + Versioning.Latest)"></script>
      

      Where Versioning.Latest is defined as follows:

       
      public class Versioning
      {
          public static string Latest
          {
              get
              {
                  return "?v=" + typeof(Versioning).Assembly.GetName().Version;
              }
          }
      }
      

      This works, however, it will cause a redownload of all javascript/css files when a new version is deployed, even if the file hasn't changed. Third party javascript libraries such as KendoUI, jQuery and date.js are unlikely to change (particularly if you're referencing a particular version of the library).

      I updated this code to use the new content bundling features that were introduced in MVC 4. Here's the steps:

      1. In the NuGet Pageage Manager Console, grab Microsoft.AspNet.Web.Optimization

      Install-Package Microsoft.AspNet.Web.Optimization

      2. Create a BundleConfig.cs and define your bundles

      public class BundleConfig
      {
          public static void RegisterBundles(BundleCollection bundles)
          {
              // Scripts
              bundles.Add(new ScriptBundle("~/bundles/kendoui").Include(
                  "~/Scripts/kendo/2012.2.913/jquery.min.js",
                  "~/Scripts/kendo/2012.2.913/kendo.all.min.js", 
                  "~/Scripts/kendo/2012.2.913/kendo.aspnetmvc.min.js"));
      
              bundles.Add(new ScriptBundle("~/bundles/thirdparty").Include(
                 "~/Scripts/jquery-ui-*",
                 "~/Scripts/jquery.formatCurrency-*",
                 "~/Scripts/jquery.watermark.min.js",
                 "~/Scripts/jquery.cross-slide.min.js",
                 "~/Scripts/jquery.cookie.js",
                 "~/Scripts/date.js",
                 "~/Scripts/browser.support.js",
                 "~/Scripts/handlebars.min.js"));
                     
              // Stylesheets
              bundles.Add(new StyleBundle("~/Content/css").Include(
                  "~/Content/themes/base/jquery-ui.css",
                  "~/Content/Site.css"));
      
              bundles.Add(new StyleBundle("~/Content/kendoui-css").Include(
                  "~/Content/kendo/2012.2.913/kendo.common.min.css",
                  "~/Content/kendo/2012.2.913/kendo.metro.min.css"));
          }
      }
      

      3. Call the bundle config from Global.asax Application_Start to register the bundles

      protected void Application_Start()
      {
         :  
         BundleConfig.RegisterBundles(BundleTable.Bundles);
         :
      }
      

      4. Update your _layout.cshtml to reference the bundles

      <link href="@Scripts.Url("~/Content/css")" rel="stylesheet" type="text/css" />
      <link href="@Scripts.Url("~/Content/kendoui-css")" rel="stylesheet" type="text/css" />
      <script src="@Scripts.Url("~/bundles/kendoui")" type="text/javascript"></script>
      <script src="@Scripts.Url("~/bundles/thirdparty")" type="text/javascript"></script>
      

      5. Update your (root)\web.config and Views\web.config to register the System.Web.Optimization namespace

      <pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
          <add namespace="System.Web.Mvc" />
          <add namespace="System.Web.Mvc.Ajax" />
          <add namespace="System.Web.Mvc.Html" />
          <add namespace="System.Web.Routing" />
          <add namespace="System.Web.Optimization" />
        </namespaces>
      </pages>
      

      6. And you're done!

      Run your application and look at the generated URLs for your CSS and JS. They contain a hash in the URL that updates when the bundle changes:

      <link href="/Content/css?v=DW4VA8QHWz0lpg-1xgEkBu9a2jl0u21oskVI8gjXdO81" rel="stylesheet" type="text/css" />
      <link href="/Content/kendoui-css?v=NOcY8mlFVB5YWf-VVYSPB17Ov0AXjKcYHOg76QV3vqQ1" rel="stylesheet" type="text/css" />
              
      <script src="/bundles/kendoui?v=YbNJGKtU_KjafyxKAw3U4QVNX021Qk1BCIWYBvui2n41" type="text/javascript"></script>
      <script src="/bundles/thirdparty?v=kArVyDzIkXBQ9DtFhv4FWSKAx1YfHKB4UlIG_6cJmVY1" type="text/javascript"></script>
      

      If we update the Site.css and rebuild, only the /Content/css has changes, all the other hashes remain the same.

      <link href="/Content/css?v=tynpQ3xqzP2PGlfCEDcWZa9rnxNaQPSPSBl2yxmF_gI1" rel="stylesheet" type="text/css" />
      <link href="/Content/kendoui-css?v=NOcY8mlFVB5YWf-VVYSPB17Ov0AXjKcYHOg76QV3vqQ1" rel="stylesheet" type="text/css" />
              
      <script src="/bundles/kendoui?v=YbNJGKtU_KjafyxKAw3U4QVNX021Qk1BCIWYBvui2n41" type="text/javascript"></script>
      <script src="/bundles/thirdparty?v=kArVyDzIkXBQ9DtFhv4FWSKAx1YfHKB4UlIG_6cJmVY1" type="text/javascript"></script>
      

      Cleaning up your client side javascript with a templating engine

      Today, I was working on a code base for a time sheeting system. It does a lot of rendering on client side like retrieving appointments and source code comments from third party system and then renders it onto the page.

      Unfortunately, the javascript that did the rendering post ajax call was something like this:

      for (var i = 0; i < wi.CheckinHistory.length; i++) {
      history += "<li><i class=\"add-note\" title=\"Add this checkin comment to the Notes field\">" + wi.CheckinHistory[i] + "</i></li>";
      }
      var item = "<span class=\"wi-link\"><a href=\"" + wi.Url + "\" target=\"_blank\">[" + wi.Id + "]</a></span>" +
      "<span class=\"wi-title\">" + wi.Title + "</span>" +
      "<span class=\"wi-state\">" + wi.State + "</span>" +
      "<span class=\"wi-project\">" + wi.ProjectName + "</span>" +
      "<span class=\"wi-checkins\"><ul>" + history + "</ul></span>" +
      "<span class=\"wi-modified\">" + "(Last Modified " + modDate + " by " + wi.ChangedBy + ")</span>";
      return item;

      This is obviously not maintainable as it's not really readable and prone to error. 

      I refactored this code and re-implemented it using a javascript templating engine. There are several around and LinkedIn has a good smackdown of several options

      I chose to use Handlebars because of the easy to read syntax and extensible (but mainly because it has an awesome logo).

      Installing and using Handlebars

      Grab it from NuGet using :

      Install-Package Handlebars.js

      Reference the handlebars.min.js in your layout/master page.

      Create your template, the javascript that builds html above will become nice and readable:

          <script id="tfsChangeSetTemplate" type="text/x-handlebars-template">
      <h3>Changesets ({{count result}})</h3>
      {{#each result}}
      <div class="wi-item">
      <span class="wi-link"><a href="{{Url}}" target="_blank">[Changeset {{Id}}]</a></span>
      <span class="wi-title">Checked In {{CommitDateFormatted}} by {{CommittedBy}} </span>
      <span class="wi-state">{{Changes}} file(s) changed</span>
      <span class="wi-checkins">
      <ul>
      <li>{{#if Comment}}
      <i class="add-note" title="Add this checkin comment to the Notes field">{{html Comment}}
      </i>
      {{else}}
      <i>No comment was entered
      </i>
      {{/if}}
      </li>
      </ul>
      </span>
      {{#if WorkItems}}
      <span class="wi-workitems">
      <ul style="list-style: none; margin-left: 20px;">
      {{#each WorkItems}}
      <li>
      <span class="wi-link"><a href="{{Url}}" target="_blank">[WI {{this.Id}}]</a></span>
      <span class="wi-title">{{Title}}</span>
      <span class="wi-state">{{State}}</span>
      <span class="wi-project">{{ProjectName}}</span>
      </li>
      {{/each}}
      </ul>
      </span>
      {{/if}}
      </div>
      {{/each}}


      </script>

      You'll notice I put in some conditional logic using the IF block and made extensive use of the EACH block to loop through the JSON data.

      The javascript to get and render the template is:

      if (data.length > 0) {
      var tfsWorkItemTemplate = Handlebars.compile($("#tfsChangeSetTemplate").html());
      var html = tfsWorkItemTemplate({ result: data });
      $tasks.append(html);
      }

      Nice and simple!

      Running a stand-up meeting using Team Foundation Service (tfspreview)

      Here at SSW, we've been transitioning all our new projects onto Team Foundation Service. It’s been a pleasure to work with in all aspects of the application lifecycle. It’s main features are:

      • A great UI for managing the tasks in a sprint
      • Cloud based hosting, so you don’t have to worry about upgrades and server hardware
      • It connects to Windows Live for authentication so you don’t need to create an Active Directory account for your clients

      Below is a video of how Microsoft use Team Foundation Service to run their internal scrum teams. It provides great insight into how the tool works, but also shows you how a standup meeting should run. The key takeaways for me are:

      • Stand-up meeting happens in a corridor
      • Each team member can easily switch the taskboard to their view (showing what they’ve worked on)
      • “Parking” issues so the stand-up is quick

      Check out Methodology - Do you do Daily Scrums (aka stand-up meetings)? for more details about how SSW runs scrums