How to use .NET WebClient synchronously and still receive progress updates

Recently, I had to implement our custom package deployment mechanism for our UI framework where packages are hosted on a web-accessible URI. During the download process, the UI should display a progress bar which updates the user on the progress the download. After download is complete, the client performs a few more steps including

  • checking file integrity against its md5 hash
  • expanding the compressed file into the destination
  • cleaning up

The implementation uses TPL; for each package that user chose to upgrade, a task is created which performs the steps above. During this process, the UI displays a progress of each task.

The download portion of the process is implemented using WebClient.DownloadFile method. It’s short and sweet, hiding most of the complexity of opening a connection, getting a response stream and reading it one chunk at a time. But its simplicity comes a shortcoming: during download the thread doing the downloading is blocked an no progress is reported, so downloading a large file would cause the user to think that the system froze, because even though the wait spinner is spinning, the progress bar is stuck. What I want is to get updates on the progress of my download while waiting for download to complete.

The WebClient class has convenient DownloadProgressChanged event which seems like a perfect candidate to help me here, so I attached a delegate to it, but it was never called. Upon careful reading of the documentation, I learned that I must use the asynchronous version of the DownloadFile mehtod, DownloadFileAsync to receive any progress updates.

While I could switch my code around to accommodate new asynchronous calling pattern, I did not want to drastically redesign my existing code. It was already relying on the TPL for asynchrony, adding another asynchronous mechanism would add more complexity to my code. So I decided to try to keep the method that deals with WebClient synchronous, but still receive updates. To do this, I would need to call the DownloadFileAsync method, but then block my thread until download completes or fails. Luckily, WebClient has the DownloadFileCompleted event which would come in handy here. Since I would block my thread after calling the DownloadFileAsync method, when the DownloadFileCompleted fires, I would unblock my calling thread. Here’s the code that implements this solution


public void DownloadFile(Uri uri, string desintaion)
{
  using(var wc = new WebClient())
  {
    wc.DownloadProgressChanged += HandleDownloadProgress;
    wc.DownloadFileCompleted += HandleDownloadComplete;

    var syncObject = new Object();
    lock(syncObject)
    {
       wc.DownloadFileAsync(sourceUri, destination, syncObject);
       //This would block the thread until download completes
       Monitor.Wait(syncObject);
    }
  }

  //Do more stuff after download was complete
}

public void HandleDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
   lock(e.UserState)
   {
      //releases blocked thread
      Monitor.Pulse(e.UserState);
   }
}

public void HandleDownloadProgress(object sender, DownloadProgressChangedEventArgs args)
{
  //Process progress updates here
}

At the end, I still use WebClient synchronously without losing the benefits that come with its asynchronous usage.

Happy Downloading!

Advertisements

Powershell -match operator

I think Powershell’s -match operator deserves its own blog post because it does so much. I’ve been automating our build process using Powershell and I gained a lot of respect for this operator. The examples below, would usually refer to the SVN url parsing.

Matching

Firstly, it returns true ($true in Powershell) if the original string matches the regex expression you passed into it. For example,

$string = "Hello"
$string -match "Hello" #return true

$string -cmatch "hello" #return false, cmatch performs case-sensitive match

$string -cmatch "[H|h]ello" #return true  

Group Matching

In addition to regular matching the -match operator will populate a special $matches variable. For example

$string = "Hello World"
$string -match "Hello"

$matches[0] #prints "Hello"

$string = "Hello World"
$string -match "(?'first'Hello)"

Name    Value                                                                                                 
----    -----                                                                                                 
first   Hello                                                                                                 
0       Hello 

Group Matching and Replacing

If you are crafty enough, you can come up with a match and replace strategy for the strings you are working with. For example, if you have to figure out the full path of the svn path if you know the name of the tag, given the current branch path, you’ll do the following:


$url = "http://subversion.url.com/project/branches/VERSION-1.0/path/to/code"
$search = "(.*)(?'branch'branches/[^/]*)(.*)" #Generates 3 groups (1,2, and branch)

$url -match $search
True

$matches

Name     Value                                                                                                 
----     -----                                                                                                 
branch   branches/VERSION-1.0                                                                                  
2        /path/to/code                                                                                         
1        http://subversion.url.com/project/                                                                    
0        http://subversion.url.com/project/branches/VERSION-1.0/path/to/code           

#use the replace method of the Match object to replace branche path with tag path:
$newUrl = $matches[0].Replace($matches['branch'], 'tags/TAG-123') #replaces branch path with new tag path

$newUrl
http://subversion.url.com/project/tags/TAG-123/path/to/code

Automate Visual Studio project management from Package Manager Console

Visual Studio 2012 and above comes with the Package Manager Console, PowerShell-based tool used to automate NuGet package management from the command line.  But you can use it to access and change solutions and projects loaded into your Visual Studio using the DTE API, same interface Visual Studio exposes to its Addins.

In this post, I will demonstrate how you can replace all references to binaries (dll or exe files) with references to projects loaded into your solution. Such operation is necessary when you need to debug the code inside the dependencies. This use case is a frequent occurrence in development shops that split their code base into core, more technie, low-level functionality and higher level business-level code. The lower-level code is encapsulated into one or more dlls and the higher-level developers reference those dlls when developing their client-facing solutions.

Usually, the lower-level code has been well designed, developed and properly unit tested to make them work as expected. But sometimes something is not right, and it’s up to the app developer, who is on the deadline to figure out why. So the app developer would check out the code for the lower-level API and would load the lower-level API  solution into their existing higher-level app solution. You can do this by

  1. right-clicking on the existing loaded solution
  2. picking Add -> Existing Project,
  3. changing the dropdown in the file picker to Solution Files (*.sln)
  4. picking the dependency solution in the dialog

The next step, would be for developers to manually replace references in your higher-level projects, removing references to the binary dll or exe files and pointing them to their equivalent projects that are now part of your solution.  This operation is tedious and error prone and is a perfect candidate for Powershell automation.   Let’s get started.

Before we begin, however, lets make it easier to separate the two sets of projects into the lower-level projects and the higher level projects.  The best way to segregate inside a single solution is to use solution folders.  So let’s create a folder called “Dependencies” and drag all the lower-level API projects under it.  You can create a solution folder by right-clicking on your solution and picking Add -> New Solution Folder.  We are ready now!

Switch to or open the Package Manager Console.  In VS2012  you can open it from TOOLS -> NuGet Package Manger -> Package Manager Console.  You should now be in the command prompt.  Here’s a script to perform such replacements.

function Update-References() 
{
	#find the solution folder called "Dependencies" where the dependency projects live
	$depFolder = $dte.Solution.Projects | where { $_.Kind -eq "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}" -and $_.Name -eq 'Dependencies' }
	if(-not $depFolder)
	{
		Write-Error "Did not find a Dependencies folder in your solution.  It must be a top-level folder";
		return -1
	}

	#Load a list of all the dependency projects into a hashtable
	$depProjects = @{}
	$depFolder.ProjectItems | % { $_.Object }  | % { $depProjects.add($_.Name, $_) }

	#load a list of all high-level projects (my projects)
	$myProjects = $dte.Solution.Projects | where { $_.Kind -eq '{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}' }

	#iterate through references in my projects and remember those that match the dependency projects.  Once matched, remove the old reference
	$projectToRefName = @{}
	foreach ($project in $myProjects.Object) 
	{ 	
		$lst = new-object system.collections.arraylist
		$projectToRefName.Add($project, $lst)
		foreach($reference in $project.References  | where { $depProjects.Contains($_.Identity) } )	 
		{ 
			$lst.add($reference.Identity)
			$reference.Remove() 
		}
	}

	#replace add a project reference for all the binary references we removed
	foreach ($project in $myProjects.Object) 
	{ 	
		$lst = $projectToRefName[$project]
        $depProjects.Values | where { $lst.Contains($_.Name) }  | % { $project.References.AddProject($_) } 
	}
}

Export-ModuleMember Update-References 

You can save this script into a script module file (*.psm1) somewhere and then use Import-Module command to load it. The Import-Module command takes the full path to the psm1 file like this:

Import-Module C:\MyPowershellModules\UpdateReferences.psm1

#if you need to make a change to the file, you will need to remove module first, because importing it again
Remove-Module UpdateReferences

How to customize column headings in DevExpress WPF GridControl

DevExpress is one of several 3rd party .NET control providers offering a full suite of controls for WPF.  Their library of widgets is widely used in many financial firms to speed up time to market and make UI development easier.  They look good and work relatively well.  But I don’t want to advertize them too much here. If you are working with their GridControl, you probably noticed how notoriously difficult it is to certain things you thought should be a lot easier.

I recently was asked to color the background of the Grid Headers and decided I would share my pain and the solution here. Unfortunately, this exercise is not for the faint-hearted, as it requires switching your WPF project to use a custom theme that you will create using their tool.

I will show how you can modify their grid’s column heading template to inject your own Attached Properties which you can then set in your Xaml. But first, you will need to switch to a custom theme which you will create using the DevEpxress Theme Editor, which you can download here: https://www.devexpress.com/Products/NET/Controls/WPF/Themes/theme-editor.xml. You can read instructions on using the tool here: https://documentation.devexpress.com/#WpfThemeEditor/CustomDocument10429. If you use their skins already then pick those that you use as a base when saving your own custom theme. Once you have your own custom theme project, and are successfully using it in your project, you are ready to proceed with the advanced part of this exercise.

Here’s an overview of what you will need to do.

  • Step 1. Create another dll project where you would define your own custom attached properties. YOu will need to compile the project and generate a DLL which you will later reference
  • Step 2. Open the theme projects that the DevExpress tool generated in Visual Studio and add references to the dll you created in step 1.
  • Step 3. Modify their Grid Header XAML templates to reference your new Attached Properties.
  • Step 4. Reference dll you had created in step 1 in your projects and set the Attached Properties you created in the GridColumn definitions.

DevExpress wants you to use 3 different brushes to color Grid Column Headers:

1. Normal Background Brush: used for normal state rendering
2. Hover Over Brush: used when mouse is over the column heading
3. Mouse Click Brush: used when mouse is clicked while over the column heading

To encapsulate all 3 states, we are going to create a custom Type called GridColHeaderBrushes in our custom dll project (step 1). I’ll be a DependencyObject defining 3 Attached properties.
Here’s the code.


    public class GridColHeaderBrushes : DependencyObject
    {
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.RegisterAttached("Background",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public static readonly DependencyProperty MouseOverProperty =
            DependencyProperty.RegisterAttached("MouseOver",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public static readonly DependencyProperty MousePressedProperty =
            DependencyProperty.RegisterAttached("MousePressed",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        public Brush MouseOver
        {
            get { return (Brush)GetValue(MouseOverProperty); }
            set { SetValue(MouseOverProperty, value); }
        }

        public Brush MousePressed
        {
            get { return (Brush)GetValue(MousePressedProperty); }
            set { SetValue(MousePressedProperty, value); }
        }
    }

In addition we would need to create another class which would contain attached properties we would use on the grid. The two files must be in the same namespace to simplify things. Notice that I also added 2 more properties to control text alignment in column headers. This is a bonus 🙂

    public static class GridColumnHeaders
    {
        public static readonly DependencyProperty HeaderBackgroundsProperty =
            DependencyProperty.RegisterAttached("HeaderBackgrounds",
            typeof(GridColHeaderBrushes),
            typeof(GridColumnHeaders));

        public static readonly DependencyProperty HorizontalHeaderAlignmentProperty =
            DependencyProperty.RegisterAttached("HorizontalHeaderAlignment",
            typeof(HorizontalAlignment),
            typeof(GridColumnHeaders));

        public static readonly DependencyProperty VerticalHeaderAlignmentProperty =
            DependencyProperty.RegisterAttached("VerticalHeaderAlignment",
            typeof(VerticalAlignment),
            typeof(GridColumnHeaders));


        public static HorizontalAlignment GetHorizontalHeaderAlignment(DependencyObject obj)
        {
            return (HorizontalAlignment)obj.GetValue(HorizontalHeaderAlignmentProperty);
        }

        public static void SetHorizontalHeaderAlignment(DependencyObject obj, HorizontalAlignment value)
        {
            obj.SetValue(HorizontalHeaderAlignmentProperty, value);
        }

        public static VerticalAlignment GetVerticalHeaderAlignment(DependencyObject obj)
        {
            return (VerticalAlignment)obj.GetValue(VerticalHeaderAlignmentProperty);
        }

        public static void SetVerticalHeaderAlignment(DependencyObject obj, VerticalAlignment value)
        {
            obj.SetValue(VerticalHeaderAlignmentProperty, value);
        }

        public static GridColHeaderBrushes GetHeaderBackgrounds(DependencyObject obj)
        {
            return (GridColHeaderBrushes)obj.GetValue(HeaderBackgroundsProperty);
        }

        public static void SetHeaderBackgrounds(DependencyObject obj, GridColHeaderBrushes value)
        {
            obj.SetValue(HeaderBackgroundsProperty, value);
        }
    }

After you compile the dll, place it somewhere near the custom theme projects that DevExpress tool created for you, then open the custom theme project in Visual Studio. Open the regular project first – the one without the “ThemeEditor” in its name.

You will need to modify the ColumnHeader.xaml file located under the folder that matches your custom theme name, not under the “Generic” folder. You will need to do the following:

  1. Add a namespace reference to the namespace containing the classes above:
    <ResourceDictionary ... 
       xmlns:customui="clr-namespace:YourCustomAssembly.Namespace;assembly=YourCustomAssembly"/>
    
  2. Find the Border classes which are used as backgrounds for grid headers. They are named
    • BackgroundBorder
    • MouseOverElement
    • MousePressedElement

    and modify their “Background” Property so they look like this below. Make sure that you replace the with the hard-coded color that was in the code before you changed it.

    <Border x:Name="BackgroundBorder" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).Background, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" cs:Name="Border_0004" />
    <Border x:Name="MouseOverElement" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).MouseOver, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" Opacity="0" cs:Name="Border_0005" />
    <Border x:Name="MousePressedElement" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).MousePressed, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" Opacity="0" cs:Name="Border_0006" /> 	
    
  3. As a bonus, add the HorizontalAlignment and VerticalAlignment properties to the Grid called “PART_Content” so it looks like this:
    <Grid x:Name="PART_Content"
      HorizontalAlignment="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HorizontalHeaderAlignment), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Left}"
      VerticalAlignment="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.VerticalHeaderAlignment), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Center}" >
    
  4. Compile and Save the project
  5. Replace the original dll that the DevExpress Theme Editor had created for your project with the new version you just compiled.

Customizing Grid Column Headers

You will need to reference the your custom dll containing the new Attached Properties in your project as well.  Once you do, you are ready to set the colors and alghment of your grid column headings. Don’t forget to add a customui namespace declaration at the top of your xaml file.

Here’s an example:

<dxg:GridControl ...>
 <dxg.GridControl.Columns>
   <dxg.GridColumn...>
      <customui:GridColumnHeaders.HeaderBackgrounds>
           <customui:DrigColHeaderBrushes Background="White" MouseOver="Green" MousePressed="Orange"/>
      </customui:GridColumnHeaders.HeaderBackgrounds>
   </dxg.GridColumn>
  ...

To control alignment you can do this:

<dxg:GridControl ...>
 <dxg.GridControl.Columns>
   <dxg.GridColumn customui:GridColumnHeaders.HorizontalHeaderAlignment="Right".../>
  ...

Calculating Moving Average (or other aggregations) using LINQ

A while ago I wrote about calculating moving average of live data using the Buffer() method in Reactive Extensions. The method creates a buffer of updates whose boundary you, as the developer specify, and provides you this running buffer with every subsequent update. The ‘current’ update would be last in the buffer, which allows you to calculate the inclusive moving average, one where the last factor is included in the results.

Recently, I had to do something similar with static data in an application which was not using RX, bud had a well-defined LINQ-based data provider. To my surprise, I found that LINQ does not provide a buffering method out of the box. To my greater surprise, I found that it was easy to implement one. The code is below.

I did not want to assume that we would only perform average aggregations, therefore, I kept my solution fairly generic. It would be up to the user of my function to code up the aggregation logic. In this post, I would demonstrate average aggregations, but the user of this function would be able to do anything with a given set of items.

First, let’s define a data structure, called Aggregation which would be used to wrap the original and add a new AggregatedValue property (line 16) that we would be populated during aggregation process. Then I define a sample TimedDataPoint which would be used to hold stock prices in my example.

public struct Aggregation<TSource, TValue>
{
	private TSource _dp;

	public Aggregation(TSource dataPoint)
	{
		_dp = dataPoint;
		AggregatedValue = default(TValue);
	}

	public TSource DataPoint
	{
		get { return _dp; }
	}

	public TValue AggregatedValue;
}

public struct TimedDataPoint<T>
{
	public DateTime Date { get; set; }

	public T Value { get; set; }

	public override string ToString()
	{
		return string.Format("{0} @ {1}", 
 			Date.ToShortDateString(), Value);
	}
}

Finally, let’s define a LINQ function that would buffer a set of items in the original set and would return a modified set with fewer items, where each item would contain an aggregation value for the buffered set.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MovingAverage.Linq
{
    public static class Aggregations
    {
        public static IEnumerable<Aggregation<TSource, TValue>> MovingAggregation<TSource, TValue>(
            this IEnumerable<TSource> source,
            Func<TSource, int, bool> shouldBuffer,
            Func<Aggregation<TSource, TValue>, TSource, IEnumerable<TSource>, TSource, int, TValue> projection)
        {
            Queue<TSource> buffer = new Queue<TSource>();
            TSource droppedItem = default(TSource);
            var runningAgg = default(Aggregation<TSource, TValue>);

            foreach (var item in source.TakeWhile(val => shouldBuffer(val, buffer.Count)))
            {
                buffer.Enqueue(item);
            }

            int skipCount = 0;
            foreach (var item in source.SkipWhile(val => shouldBuffer(val, skipCount++)))
            {
                //Add to the buffer for inclusion
                buffer.Enqueue(item);
                runningAgg = new Aggregation<TSource, TValue>(item) { AggregatedValue = projection(runningAgg, item, buffer, droppedItem, buffer.Count) };
                droppedItem = buffer.Dequeue();

                yield return runningAgg;
            }
        }
    }
}

To claim success, I need to test the function. To play with real numbers, let’s get the history of weekly MSFT stock returns from yahoo. Let’s export this data to excel and create set of moving averages of Close price to test our data. Here’s what the spreadsheet should look like:

Moving Average in Excel

We can then create a sample dataset to represent the original closing prices. We can then test our function my creating a moving average calculation on the dataset. The code is below.

First, I create the dataaset, then I iterate through the dataset, piping the data into my aggregator function, which in turn calls me back and asks me to make 2 important decisions:

1. Should it continue buffering? (Lines 60 and 71)
2. Given a buffer provide an aggregation.(Line 61 in the first example and lines 74-82 in the second)

The output of the sample should match the 6-day Avg column in the attached spreadsheet.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MovingAverage.Linq;

namespace MovingAverage
{
    class Program
    {
        static void Main(string[] args)
        {
            var datapoints = new List<TimedDataPoint<decimal>>()
      {
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 1,2), Value = 36.91m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 1,6), Value = 36.04m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 1,13), Value = 36.38m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 1,21), Value = 36.81m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 1,27), Value = 37.84m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 2,3), Value = 36.56m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 2,10), Value = 37.62m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 2,18), Value = 37.98m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 2,24), Value = 38.31m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 3,3), Value = 37.90m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 3,10), Value = 37.70m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 3,17), Value = 40.16m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 3,24), Value = 40.30m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 3,31), Value = 39.87m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 4,7), Value = 39.21m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 4,14), Value = 40.01m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 4,21), Value = 39.91m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 4,28), Value = 39.69m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 5,5), Value = 39.54m},
          new TimedDataPoint<decimal> 
          { Date = new DateTime(2014, 5,12), Value = 39.83m},
      };


            foreach (var item in datapoints.MovingAggregation<TimedDataPoint<decimal>, decimal>(
                    (item, count) => count < 5,
                    (seed, item, buffer, droppedoff, count) => buffer.Average(val => val.Value)))
            {
                Console.WriteLine("Value {0} MovingAverage {1:F3}",
                                    item.DataPoint,
                                    item.AggregatedValue);
            }

            Console.WriteLine("==================================");

            foreach (var item in datapoints.MovingAggregation<TimedDataPoint<decimal>, decimal>(
                    (item, count) => count < 5,
                    (aggSoFar, item, buffer, droppedoff, count) =>
                    {
                        if (aggSoFar.AggregatedValue == 0)
                        {
                            return buffer.Average(val => val.Value);
                        }
                        else
                        {
                            return aggSoFar.AggregatedValue +
                              (item.Value - droppedoff.Value) / count;
                        }
                    }))
            {
                Console.WriteLine("Value {0} MovingAverage {1:F3}",
                                    item.DataPoint,
                                    item.AggregatedValue);
            }
        }
    }
}

I found this aggregator function very useful. Hope you will too.

How to lazy-load almost anything in AngularJS

I was asked to build a module web framework which would allow various teams to plug-in their parts as they become available with none or minimal affects on other existing parts. Each new part could be represented by a tab or a menu section. While each part relies on some common/shared services, frameworks or components, each also has unique dependencies. For example, all parts can rely on jquery, but the first part relies on Highcharts library for charting, while another part uses SlickGrid to represent data. Firstly, I had to pick libraries and frameworks to crate a foundation for the site. Foundation components would be either be used directly or relied upon by all the parts in the system. After careful evaluation, I picked the following

  • jQuery
  • AngularJS
  • RequireJS
  • BootstrapUI with Angular extensions

To wire up Angular and Require, I used the seed example found here: https://github.com/tnajdek/angular-requirejs-seed. Thanks to the configuration file used by RequireJS, my main index.html file only has a single script dependency:

<script data-main="app/js/main" src="app/lib/requirejs/require.js"></script>

The app/js/main.js file defines all the script dependencies required by the website. This file would need to be modified as dependencies get added by the parts of the system. For example, if a new part coming online requires another library, that library would have to be registered in this file along with its dependencies.

require.config({
	paths: {
		jquery: '../lib/{path to jquery}',
		angular: '../lib/{path to angular}',
		angularRoute: '../lib/{path to angular ui router}',
		angularBootstrap: '../lib/{path to bootstrap ui}',
		highcharts: '../lib/{path to highcharts}',
		'highcharts-export': '../lib/{path to highcharts export}',
		'highcharts-drilldown': '../lib/{path to highcharts drilldown}',
		'jquery-ui':'../lib/{path to jquery ui}',
		'jquery-drag-and-drop': '../lib/{path to jquery drag-n-drop}',
		'slickgrid-core': '../lib/{path to slickgrid core}',
		'slickgrid-cellselectionmodel': '../lib/{path to slickgrid cellselection model}',
		'slickgrid-rowmovemanager': '../lib/{path to slickgrid rowmovemanager}',
		'slickgrid-grid':'../lib/{path to slickgrid}'
	},

//Dependencies defined below
	shim: {
		'angular' : {
			'exports' : 'angular',
			'deps': ['jquery']
	},
		'angularRoute': ['angular'],
		'angularBootstrap': ['angular'],
		'highchart-theme': ['highcharts'],
		'highcharts-export': ['highcharts'],
		'highcharts-drilldown': ['highcharts'],
		'jquery-ui':['jquery'],
		'jquery-drag-and-drop':['jquery-ui'],
		'slickgrid-core':['jquery-drag-and-drop'],
		'slickgrid-cellselectionmodel':['slickgrid-core'],
		'slickgrid-rowmovemanager':['slickgrid-core'],
		'slickgrid-grid':['slickgrid-cellselectionmodel', 'slickgrid-rowmovemanager']
	},
        //Not sure about the significance of this piece below
	priority: [
		'angular'
	]
});

//After configuration, let's go running the app!

//http://code.angularjs.org/1.2.1/docs/guide/bootstrap#overview_deferred-bootstrap
window.name = 'NG_DEFER_BOOTSTRAP!';

require(['angular',
	'app',
	'app-config',
	'controllers'], function(angular, app) {
	'use strict';

	angular.element().ready(function() {
		angular.resumeBootstrap([app['name']]);
	});
});

So we start by requiring Angular, App, App-Config and Controllers, which initializes angular and runs it. Most of the standard angular code has been moved into modules according to AMD specs (https://github.com/amdjs/amdjs-api/wiki/AMD) used by RequireJS. The require() function call above first specifies that it needs to load angular module (defined above), app, app-config and controllers modules, which are not defined. If not explicitely defined, RequireJS will attempt to locate these modules in the same directory as this main.js file inside files whose names would same as names of the modules with ‘.js’ appended. So I must have these files:

  • app.js
  • app-config.js
  • controllers.js

Lets’ take a look at those: app.js

define ([
         'angular',
         'angularRoute',
         'angularBootstrap',
         ], function(angular) {

	'use strict';

	var myApp = angular.module('myApp', ['ui.bootstrap', 'ui.bootstrap.tpls', 'ui.router']);

	return myApp;
});

Notice that I am returning myApp from the app module. That allows me to reference it later. For example, if you look back to the main.js code, you’ll see that a parameter named app is being passed into function inside require() call (line 52). That app is the same as myApp being returned by the app module function (line 11). I need to have access to the app because I reference it from all other modules that depend on angular. The rest of the configuration is done inside the app-config module app-config.js

define(['app',
        'services/site-definition-service',
        'services/lazy-loader']
		, function(app) {

app.config(['$stateProvider', '$controllerProvider', '$urlRouterProvider', '$httpProvider', '$provide', '$compileProvider',
	                    function (stateProvider, cp, urlRouterProvider, httpProvider, provide, compProvider) {

		//These would be used later for lazy-loading controllers, directives and services
		app.$stateProvider = stateProvider;
		app.$controllerProvider = cp;
		app.$provide = provide;
		app.$compileProvider = compProvider;

		urlRouterProvider.otherwise('/');
...

The general function behind omitted code above is to declare states for your website. Each state would represent a part of the site that other developers would contribute. You can postpone defining all states until later by having a service retrieve your site configuration and register states based on configuration you specify in the service response. Look here for more info: . Controllers that you are declaring during normal bootstrapping of Angular site can simply be declared inside AMD modules such as this example below. Because controllers module is referenced as a dependency at startup, we can do this: controllers.js


define(['angular', 'app', './services/site-definition-service'], function(angular, app) {

	'use strict';

	app.controller('SomeController', ['$scope',
	                                 function (scope) {
		...
	}]);

Notice that the controller references something called site-definition-service. This is another javascript module which defines a service necessary to retrieve definition of the site. Such definition is a simple JSON structure which allows developers to configure states. As part of state configuration you can specify a resolve {} directive to resolve additional dependencies. Dependencies could be specified as an array of module names, which RequireJS would recognize when you configure them back in the main.js file. Here’s an example lf lazy-loading state registration:


define(['app',
        'services/lazy-loader'], function(app) {

//app.$stateProvider is used to lazy-register states
app.$stateProvider.state('stateName', {
			url: '/myPart',
			resolve: {
				'loadDependencies': function ($stateParams, LazyLoader) {
					return LazyLoader.loadDependencies('stateName');
				},
			},

and here’s what lazy loading service code would look like: lazy-loader.js

  
 define(['angular', 'app', 'require', './services/site-definition-service'], function (angular, app, requirejs) {

	'use strict';
	app.service('LazyLoader', ['$cacheFactory', '$http', '$rootScope', '$q', 'SiteDefinitionService',
                      function (cacheFactory, http, rootScope, q, siteDefService) {
             var self = this;

             this.loadDependencies = function(stateName) {            

               var deferred = q.defer();
               http.get('rest/sitedefinition/' + stateName).success(function (data, status, headers, config) {
                  var deps = data.dependencies; //array
                  if(deps &amp;&amp; deps instanceof Array) {
                     loadDependenciesFromArray(deps, deferred);
                  } else {
                     deferred.resolve();
                  }
               });

               return deferred.promise();
            }

		this.loadDependenciesFromArray = function(depArr, deferred){
			requirejs(depArr, function() {
				deferred.resolve();
			});

	}]);
});

Examples of lazy-loading dependencies

Directives

custom-directive.js

define(['app'], function(app) {

	'use strict';
	//app.$compileProvider is used to lazy-register directives
	app.$compileProvider.directive('customDirective', 
    ...

Services

part2-service.js

define(['app'], function(app) {
	'use strict';

	//app.$provide is used to lazy-register services
	app.$provide.service('Part2Service', 
    ...

Controllers

part2-controller.js

define(['app'], function(app) {

	'use strict';
	//app.$controllerProvider is used to lazy-register controllers.  Part2Service was loaded as a dependency earlier
	app.$controllerProvider.register('Part2Controller', ['$scope', 'Part2Service',
               function (scope, svc) {
    ...

To lazy load CSS files you’ll need a custom directive. I’ll cover this in another post. This post has gotten big and convoluted. If something is unclear, please comment, I’ll adjust the content as necessary. Thank you.