Tag Archives: JavaScript

Formatting Dates with Knockout.js

I am working on a couple of Silverlight projects using Microsoft Prism and found the MVVM pattern very powerful. The team at work is developing a single page HTML/Javascript application that is using Knockout as the MVVM framework. I wanted to bind a date using a custom format ‘dddd, MMMM dd, yyyy’. Since JavaScript doesn’t have great support for formatting dates I pulled in Date.js.

I am familiar with using IValueConverter in Silverlight to convert values during binding. I was really happy to find a the custom binding feature in Knockout called ‘bindingHandlers‘. I was able to write a quick bindingHandler to format the date using date.js.

Checkout the Demo

Here is the custom binding for formatting dates using date.js:

ko.bindingHandlers.dateString = {
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor(),
            allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var pattern = allBindings.datePattern || 'MM/dd/yyyy';
        $(element).text(valueUnwrapped.toString(pattern));
    }
}

Here is the binding in page:

Thursday, April 05, 2012
0.00

JavaScript: Using setInterval Within an Object

I had a need to use window.setInterval(code, delay) to a call a method on a JavaScript object every few seconds to rotate some content. However, I found that once setInterval called the code in the callback ‘this’ referenced ‘window’. However, I needed ‘this’ to reference the object that was called by setInterval.

ContentTabs = {
  init: function () {
    setInterval(this.rotate, 5000);
  },
  rotate: function (){
    // this === window
  }
}

After some research I found I was having a closure issue. The solution was to use a ‘self’ variable whose scope is within the closure of the object and wrap the callback code, so it referenced the ‘self’ variable.

ContentTabs = {
  init: function () {
    var self = this;
    setInterval(function() {
       self.rotate();
    }, 5000);
  },
  rotate: function (){
    // this === ContentTabs
  }
}

How to show an InfoWindow for multiple Markers using Google Maps v3

Recently I have been working on an interactive map for the City of Aliso Viejo, CA using Google Maps.   I chose to use version 3 of the Google Maps API, because version 2 was deprecated on May 19, 2010.  I was happy to discover I no longer needed an API.  However, I did not anticipate how the changes to the MVC framework affected things.  My first task was to display multiple markers on a map, each marker having its own InfoWindow.  I found plenty of v2 examples, but could not find a really good v3 example.   Here is my solution to showing an InfoWindow for multiple markers using Google Maps v3.

In v2 GInfoWindow does not have a constructor, so an info window is displayed by calling openInfoWindow() or openInfoWindowHtml() on the GMap2 object.

GEvent.addListener(marker, 'click', function() {
    var markerPosn = new GLatLng(place["posn"][0], place["posn"][1]);
    map.openInfoWindowHtml(markerPosn ,buildPlaceInfoHtml(place),{maxWidth:275});
}); 

Things have changed significantly in Google Maps v3.  The InfoWindow now has a constructor and openInfoWindow()or openInfoWindowHtml() have been removed.  Initially I was lost trying to get each marker to show an InfoWindow with its own content.    I then read the following from the Google Maps v3 documentation about InfoWindow overlays.

InfoWindows may be attached to either Marker objects (in which case their position is based on the marker’s location) or on the map itself at a specified LatLng. If you only want one info window to display at a time (as is the behavior on Google Maps), you need only create one info window, which you can reassign to different locations or markers upon map events (such as user clicks).

Realizing I only needed one InfoWindow object. I refactored my code to create a single InfoWindow and then set  the InfoWindow content when a marker was clicked and then attach it to the appropriate marker.

var infoWindow = new google.maps.InfoWindow({});
var markers = new Array(); 

function setMarkers(map, locations, areaId) {
     for(var i = 0; i < locations.length; i++) {
         var location = locations[i];
         var latlng = new google.maps.LatLng(location.Location[0], location.Location[1]);
         var marker = new google.maps.Marker({
             position: latlng,
             map: map,
             shadow: createMarkerShadow(location.MarkerShadow),
             icon: createMarkerImage(location.MarkerImage),
             shape: markerShape,
             title: location.Name
         });
         marker.set('location', location);
         google.maps.event.addListener(marker, "click", function(event) {
            var area = this.get('location');           
            var infoWindowHtml = parseTemplate($("#MarkerTemplate").html(), { location : location} );
            infoWindow.setContent(infoWindowHtml);
            infoWindow.open(map, this);
         });
         markers.push(marker);
     }
}

function clearMarkers() {
     infoWindow.close();
     for(var i = 0; i < markers.length; i++) {
         markers[i].setMap(null);
     }
     markers.length = 0;
}

This new code iterates through an array of locations and creates a marker for each, attaches the location object, and adds an click event listener to each marker.  Attaching the location object to the marker is the key to changing the InfoWindow content when the marker is clicked. 

marker.set('location', location);

This is made possible because the InfoWindow is built on the MVCObject prototype, which exposes accessor methods.  When a marker click event fires the location object can be retrieve using the get accessor.  Now the content of the InfoWindow can be updated from the location object and attached to the marker that was clicked.

 google.maps.event.addListener(marker, "click", function(event) {
    var area = this.get('location');           
    var infoWindowHtml = parseTemplate($("#MarkerTemplate").html(), { location : location} );
    infoWindow.setContent(infoWindowHtml);
    infoWindow.open(map, this);
 });

YUI Compressor and NAnt

I have been working on creating a DotNetNuke Skin for AthleticHost.com.   In order to simplify deployment of the skin I wanted to add it as an extension to DNN rather that upload it via FTP.   I choose to use NAnt to build the extension install file.   In an effort to increase page performance I decided that the CSS and JavaScript files should be compressed and choose the YUI Compressor for the job.

I created two targets.  One to minify the JavaScript and another for the CSS.

<target name="JavaScript.minify">
<foreach item="File" property="filename">
  <in>
    <items>
      <include name="${temp.dir}/**/js/**/*.js"/>
    </items>
  </in>
  <do>
    <echo message="${filename}" />
    <exec program="java" workingdir="${YUICompressor.dir}">
      <arg value="-jar" />
      <arg value="yuicompressor-2.4.2.jar" />
      <arg value="--type" />
      <arg value="js" />
      <arg value="-o" />
      <arg value="${filename}.min" />
      <arg value="${filename}" />
    </exec>
    <move file="${filename}.min" tofile="${filename}" overwrite="true" />
  </do>
</foreach>
</target>
<target name="Css.minify">
<foreach item="File" property="filename">
  <in>
    <items>
      <include name="${temp.dir}/**/*.css"/>
    </items>
  </in>
  <do>
    <echo message="${filename}" />
    <exec program="java" workingdir="${YUICompressor.dir}">
      <arg value="-jar" />
      <arg value="yuicompressor-2.4.2.jar" />
      <arg value="--type" />
      <arg value="css" />
      <arg value="-o" />
      <arg value="${filename}.min" />
      <arg value="${filename}" />
    </exec>
    <move file="${filename}.min" tofile="${filename}" overwrite="true" />
  </do>
</foreach>
</target>

The “Deploy” target then calls the “Css.minify” and “Javascript.minify”

<call target="JavaScript.minify" />
<call target="Css.minify" />

Download the full NAnt script.

Resouces:
http://developer.yahoo.com/yui/compressor/
http://nant.sourceforge.net/

JavaScript Variable Pattern

To help make writing and maintaining JavaScript easier in ASP.NET I have started to use a pattern that isolates the JavaScript variables away from the core script.  By doing this I can avoid a bunch of string concatenation in my server side code and embedded code blocks in my .aspx page when rendering dynamic JavaScript.  The core JavaScript can be placed into a .js file.  The ASP.NET page is now only responsible for rendering a list of updated variables that the core script expects.

To demonstrate consider a set of jQuery tabs that contains forms on each tab.  When a form is submitted from a particular tab the tab should be selected after the form posts back.  Using embedded code blocks the JavaScript would be. 

<script type="text/javascript">
    jQuery(function($) {
        $("#tabs").tabs();
        $("#tabs").tabs('select', <%=selectedTab %>);
    });
</script>

While this looks pretty clean it prevents us from putting this JavaScript into a .js file.  Applying the variable pattern the JavaScript can be refactored to the following and placed into a .js file.

jQuery(function($) {
    $("#tabs").tabs();
    $("#tabs").tabs('select', selectedTab);
});

The page is now only responsible for rendering the ‘selectedTab’ variable.

<script type="text/javascript">
    var selectedTab = 3;
</script>

I have created a JavaScriptVariable class to assist in rendering the JavaScript variables.   The JavaScript block is rendered to the page using ScriptManager.RegisterStartupScript to ensure the variable are available to scripts.  In addition, the ‘JavaScriptVariables’ member variable and the RenderJavaScriptVaraibles() method could be added to a base Paqe class.  Here is an example using JavaScriptVariable to render the variables for example above.

    IList<JavaScriptVariable> JavaScriptVariables = new List<JavaScriptVariable>();
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        JavaScriptVariables.Add(new JavaScriptVariable("selectedTab", selectedTab.ToString(), "{0}"));
        ScriptManager.RegisterStartupScript(this, typeof(AspNetDiag), "AspNetDiagVars.js", RenderJavaScriptVariables(), true);
    }

    private string RenderJavaScriptVariables()
    {
        StringBuilder js = new StringBuilder();
        foreach (var jsVar in JavaScriptVariables)
        {
            js.AppendFormat("var {0} = {1};", jsVar.Name, jsVar.GetFormatedValue());
        }
        return js.ToString();
    }

Finally the source for JavaScriptVariable.  

class JavaScriptVariable
{
    public string Name { get; set; }
    public string Value { get; set; }
    public string Format { get; set; }

    public JavaScriptVariable(string name, string value) : this(name, value, "\"{0}\"") { }
    public JavaScriptVariable(string name, string value, string format)
    {
        Name = name;
        Value = value;
        Format = format;
    }

    public string GetFormatedValue()
    {
        return String.Format(Format, Value);
    }
}

Calling a button click from JavaScript in ASP.NET

ASP.NET web forms page have a JavaScript method for handling PostBacks called “__doPostBack”.  This function is used to submit button clicks back to the server.

function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

The cool thing is that you can take advantage of the same methods that the framework uses to generate javascript for button clicks.  Use the following in your code file to generate the JavaScript that performs the PostBack. Using this method will ensure that the proper ClientID for the control is used.

protected string GetLoginPostBack()
{
    return Page.ClientScript.GetPostBackEventReference(btnLogin, string.Empty);
}

Then in the ASPX page add a javascript block.

<script language="javascript">
function btnLogin_Click() {
  <%= GetLoginPostBack() %>;
}
</script>

The final javascript will be rendered like this.

<script language="javascript">
function btnLogin_Click() {
  __doPostBack('btnLogin','');
}
</script>

Now you can use “btnLogin_Click()” from your javascript to submit the button click to the server.