Knockout.JS: AsDictionary

I frequently find that I have an array of objects in JavaScript that I want to display in a particular order and also have the ability to quickly locate an object by an ID or a key (and not use the indexOf function). As my recent project is using Knockout.JS, I decided to throw together a function that makes having keyed lookups based on an array simple to maintain.

Here’s an example ViewModel definition:

var ViewModel = function () {
    this.uniqueNumber = ko.observable();
    this.list = ko.observableArray([]);      
    this.list_by_keys = this.list.asDictionary('id');
};

The definition includes an array, a unique number (more on that a bit later), the list (to which the code will bind the UI to), and finally the keyed list.

After a few attempts at a good name, I settled for something that I hated the least. Smile In any case, usage is simple.

After creating the observable array:

this.list = ko.observableArray([]); 

The code creates a second field which will contain all of the objects in the original array, but in a quickly accessible index (thanks to the nature of JavaScript objects).

this.list_by_keys = this.list.asDictionary('id');

In the preceding line, the asDictionary function (which I’ve added to the observableArray definition as you’ll see below) is used and passed the string ‘id’. The ‘id’ is the name of the property of the JavaScript object that is later added to the list that will contain the key (the primary key, although it’s not checked for duplicates).

As you’ll note below, an instance of the ViewModel is created and bound to the UI.

var vm = new ViewModel();
ko.applyBindings(vm);

$("#btnAdd").on("click", function () {
    var id = vm.uniqueNumber.inc()();
    vm.list.push({ id: id, 
                   time: new Date().toLocaleTimeString() }); });

With a click of a button (using jQuery syntax), a new sample object containing an ‘id’ and ‘time’ property is added to the master list. When the new object is added, the asDictionary code is executed. Why? Because of the use of the computed function as shown below. Knockout.JS has computed observables which automatically track dependencies and execute any time that the source property changes. In this case, it’s tracking the “this” object, which just happens to be the observableArray (list).

ko.observableArray.fn.asDictionary = function (keyName) {
    return ko.computed(function () {
        var list = this() || [];    // the internal array
        var keys = {};              // a place for key/value
        ko.utils.arrayForEach(list, function (v) {
            if (keyName) {          // if there is a key
                keys[v[keyName]] = v;    // use it
            } else {
                keys[v] = v;
            }
        });
        return keys;
    }, this);
};

The function loops through each of the elements of the array and stores each object by the key (if provided, otherwise by the value).  Unfortunately, because there are many ways to adjust an array in JavaScript, this isn’t as efficient as I’d like. Every time something is added to the array, the entire “dictionary” is recreated. While this isn’t terrible in reasonable cases, it’s still a bit annoying. You could add a bit of code to disable the rebuilding conditionally though if performance is going to be a big concern.

I also was experimenting with a unique number generator. It’s really quite dumb, but I ‘m posting in nonetheless.

ko.observable.fn.inc = function (incExtra) {
    incExtra = incExtra || 1;            
    var current = this() || 0;
    current += incExtra;            
    this(current);
    return this;
};

ko.observable.fn.dec = function (decExtra) {
    return ko.observable.fn.inc(decExtra || -1);
};

To use it and retrieve the value, call it like this:

var id = vm.uniqueNumber.inc()();

imageThe odd syntax calls the inc (increment) function which returns the original object (in support of chaining). Then, to get the value, it calls the properties’ getter function (the second set of parentheses).  (As I said, it was just messing around).

The HTML for the data binding looked like this:

<div class="log" data-bind="foreach: list" >
    <div class="item">
        <span data-bind="text: id" class="id"></span>
        <span data-bind="text: time"></span>
    </div>
</div>

Some code from SnugUp.Browser (an album browser for SmugMug)

I’ve been doing some tinkering recently with SmugMug again.

Through testing SnugUp for the past 4 years, I’ve made quite the mess of my SmugMug account. Literally hundreds of poorly organized and often completely junk albums. I’ve been wanting to clean it up, but SmugMug’s UI for that is so obnoxiously slow and tedious that I decided I wanted to write a tool to make it easier to manage.

Admittedly, given the amount of time I’ve spent on writing the tool (which I’m not yet finished with), I could have cleaned up my SmugMug account dozens of times. However, with my developer hat on, I thought, how fun would that be? Smile

 

image

So, I’ve started to create the SnugUp.Browser. It’s a bit of an interesting beast in that it is a Windows application that hosts a web browser to display its UI. I’d tried using WPF/XAML and just couldn’t get the look I was wanting in a reasonable amount of time and effort.

<RANT>WPF desperately needs a VirtualizingWrapPanel. Microsoft needs to ship it. </RANT>

So, I created a WinForms project to host the IE Web Browser. (Arrgh, the WPF WebBrowser sucks still. It’s not nearly as feature complete as the one from WinForms!). Internally, the web pages are served via an HttpListener.

try { int portSuggest = GetAvailablePort(); portSuggest = 40000; _listener.Prefixes.Add(string.Format("http://localhost:{0}/", portSuggest)); _listener.Start(); while (maxConnections-- > 0) { _listener.BeginGetContext(HandleRequest, _listener); } } catch (Exception ex) { Debug.WriteLine(ex); }

The HandleRequest method responds in two ways:

  1. Respond with JSON data (as if it were a web service). 99% of the code needed to access SmugMug’s APIs was already written in a C# library I wrote for SnugUp.
  2. Respond with binary data, providing a “proxy” to a service. This was needed to handle downloading images from the application. When the request was directly made from the WebBrowser to SmugMug, it was refused as the http-referer header was not a valid source apparently (SmugMug didn’t like “http://localhost:####” as the referrer.
resourceRequested = context.Request.Url.LocalPath.Replace('/', '.'); if (resourceRequested.StartsWith(".")) { resourceRequested = resourceRequested.Substring(1); } Debug.WriteLine(string.Format("Requested: {0}", resourceRequested)); using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(string.Format("SnugUp.Resources.{0}", resourceRequested))) { if (stream == null) { //context.Response.StatusCode = 404; if (url.LocalPath.StartsWith("/proxy")) { var proxyUrl = url.Query.Substring("?url=".Length); extension = Path.GetFileName(proxyUrl); try { WebClient client = new WebClient(); byte[] buffer = client.DownloadData(proxyUrl); context.Response.Headers.Add("Expires", DateTime.UtcNow.AddDays(30).ToString("R")); context.Response.ContentType = GetContentType(context, extension); context.Response.OutputStream.Write(buffer, 0, buffer.Length); } catch { context.Response.StatusCode = 404; } } } else { string contentType = GetContentType(context, extension); context.Response.ContentType = contentType; stream.CopyTo(context.Response.OutputStream); } // Close the Response to send it to the client. // } }

If the resource can’t be found as an Embedded Resource, it tries a proxy.

I built a tiny router for the web services so that I could easily plug-n-play new functionality:

public virtual object Route(string path, dynamic data) { if (path.StartsWith("/")) { path = path.Substring(1); } var paths = path.Split('/'); if (paths.Length > 0) { var controllerName = paths[0]; var controllerType = Type.GetType(string.Format("SnugUp.Controller.{0}Controller", controllerName), false, true); if (controllerType != null) { var instance = InitializeControllerInstance(controllerType); // default? if (paths.Length > 1) { MethodInfo method = controllerType.GetMethod(paths[1]); if (method != null) { try { object results = method.Invoke(instance, new object[] {data}); return results; } catch (Exception ex) { Debug.WriteLine(ex); } } } } } return null; }

It just looks up a request dynamically, maps to a method, and calls it (the response is eventually serialized as JSON).

For UI, I’m currently using Bootstrap (although that may be removed), Knockout.JS, and jQuery. I’m doing a bit of trickery to make sure that only what’s visible on the screen is loaded (even when the user scrolls up and down … it’s pretty smart).  I’ll likely post more about that in a future update.

The app is becoming larger… and bloated a bit when compared to my original needs. Smile

 

image

It now can show the images from the gallery as well. Smile

image

WPF & System.Windows.Baml2006.TypeConverterMarkupExtension "The image format is unrecognized"

SNAGHTML4534e6a2If you recently added an icon to your WPF project (any .NET version, including .NET 3.5, and .NET 4.0) and it has support for an alpha channel (often referred to as the Vista icon format), stop. Why? Your WPF application won’t run on the latest service pack of XP as it’s not capable of decoding the format unfortunately. It’s a very frustrating error and a stupid "feature that Microsoft overlooked. I’ve hit this a few times unfortunately.

To fix, remove all of the alpha channel images from the ICO file and recompile.

Alternative to ApplicationSettings in .NET

After dealing with lost settings, an unclear upgrade path, and my own confusion surrounding the magic of Settings in a .NET client application, I decided to build my own.

You’re probably familiar with this UI in Visual Studio. It hasn’t changed much since it was first created:

image

A list of properties, data type, scope and a default value. Admittedly, it makes things simple. However, with my WPF .NET application that I created a few years ago (SnugUp), I’ve always been troubled by the magic of the settings. It was too easy to get in a situation where a user would loose their settings doing uninstalls, reinstalls, upgrades.

While I’m sure it’s possible to make the built in settings classes to work, it wasn’t worth the effort for me to understand them and learn what the nuances of where they’re placed, how to do a decent upgrade, how not to loose them, etc.

In the SnugUp WPF UI, the code uses a two-way bindings to directly edit the settings of the application (like: "{Binding AppSettings.DebugMode}"). It was simple, and all I needed. It’s handy that ApplicationSettingsBase implements the INotifyPropertyChanged interface which WPF needs for simple two-way data bindings.

My solution, which I admit is heavier than the original as it requires a large additional assembly is to use JSON.NET as the serializer/deserializer for a new settings class I created.

So, the basic pattern:

   1: public class ApplicationSettings : INotifyPropertyChanged

   2: {

   3:     public event PropertyChangedEventHandler PropertyChanged;

   4:  

   5:     private bool _debugMode;

   6:     private string _albumNameFormat;

   7:     private string _extraFileExtensions;

   8:     private bool _automaticRun;

   9:     private string _galleryCreationSubCategory;

  10:     private bool _filenameOnlyCheck;

Properties created the standard way for INotifyPropertyChaged:

   1: private DateTime _nextUpdateCheck;

   2: public DateTime NextUpdateCheck

   3: {

   4:     get { return _nextUpdateCheck; }

   5:     set

   6:     {

   7:         if (_nextUpdateCheck != value)

   8:         {

   9:             _nextUpdateCheck = value;

  10:             RaisePropertyChanged("NextUpdateCheck");

  11:         }

  12:     }

  13: }

I wanted a predictable path for storing settings (so it would be easy to document and backup for users). I used the AssemblyCompany attribute and the AssemblyProduct attribute as the folder names:

   1: internal static string GetSettingsDirectory()

   2: {

   3:     string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

   4:     var attrs = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);

   5:     if (attrs.Length == 1)

   6:     {

   7:         path = Path.Combine(path, ((AssemblyCompanyAttribute)attrs[0]).Company);

   8:  

   9:     }

  10:     attrs = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);

  11:     if (attrs.Length == 1)

  12:     {

  13:         path = Path.Combine(path, ((AssemblyProductAttribute)attrs[0]).Product);

  14:     }

  15:     return path;              

  16: }

In this WPF application, in the AssemblyInfo.cs file, the attributes are set as follows:

   1: [assembly: AssemblyCompany("WiredPrairie.us")]

   2: [assembly: AssemblyProduct("SnugUp")]

On my machine, that maps to this path:

d:\Users\Aaron\AppData\Roaming\WiredPrairie.us\SnugUp\

Loading settings then is straightforward using JSON.NET:

   1: public static ApplicationSettings Load(string filename)

   2: {

   3:     ApplicationSettings settings = null;

   4:     var directory = GetSettingsDirectory();

   5:     var path = Path.Combine(directory, filename);

   6:  

   7:     if (File.Exists(path))

   8:     {

   9:         string fileData = File.ReadAllText(path);

  10:         try

  11:         {

  12:             settings = JsonConvert.DeserializeObject<ApplicationSettings>(fileData, new JsonSerializerSettings { });

  13:         }

  14:         catch { }

  15:     }

  16:     if (settings == null)

  17:     {

  18:         settings = new ApplicationSettings();

  19:         SetDefaults(settings);

  20:         // initialize settings once

  21:         Save(settings, filename);

  22:     }

  23:     return settings;

  24: }

In my code, if the settings file didn’t exist or fails to serialize into something meaningful, a new settings file is created with a few defaults. (I haven’t decided what to do when there’s an exception when reading the file, hence the empty catch).

Saving the settings is just as easy:

   1: public static void Save(ApplicationSettings settings, string filename)

   2: {

   3:     Debug.Assert(settings != null);

   4:     var directory = GetSettingsDirectory();

   5:     var path = Path.Combine(directory, filename);

   6:  

   7:     JsonConvert.SerializeObject(settings);

   8:  

   9:     if (!Directory.Exists(directory))

  10:     {

  11:         Directory.CreateDirectory(directory);

  12:     }

  13:  

  14:     var fileData = JsonConvert.SerializeObject(settings, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate });

  15:     try

  16:     {

  17:         using (StreamWriter writer = File.CreateText(path))

  18:         {

  19:             writer.Write(fileData);

  20:             writer.Close();

  21:         }

  22:     }

  23:     catch { }

  24: }

The SerializeObject method returns a string which is then written to a file using a StreamWriter.

I added a Save method to the instance of the ApplicationSettings:

   1: public void Save()

   2: {

   3:     ApplicationSettings.Save(this);

   4: }

This preserved the functionality that exists in the built in Settings support in .NET (which was being used in my application).

By keeping all of the property names the same and making a few tweaks to the types of some fields in my application, I had swapped out the entire “settings” infrastructure in about 45 minutes.

I’m planning some other JSON activities within my application, so the overhead of using JSON.NET is acceptable.

The best part of this alternative is that there isn’t any magic. It’s all easy to manage. Further, I can easily modify my installer to properly handle/update, etc., the settings file with just a few clicks.

I’m not going back to the built-in .NET settings support again. I’ve learned my lesson.  Smile

What have you done for “user” settings?

.NET API for Nest Thermostat

I just finished a preliminary read-only (think version 0.1) wrapper around the Nest Thermostat API that is used by their mobile phone and web applications. As Nest doesn’t have a formal API yet, the code could break at any time and may not be suitable for any use. However, it is working today. Smile

The project is hosted on GitHub. It uses JSON.NET for parsing the return values from the Nest servers.

There are three projects, with the lib containing the assembly that is used by the two test applications. One is a console app and the other a simple WPF application:

SNAGHTML88bff0b3

(My thermostats are named Zero, One, and Two).

If there’s interest, I plan on adding some methods to the library which allow modification of data (such as the current temperature), and ideally, support for live updates from the devices if I can make sense of the data that is returned.

FYI: I’ve now written a Node version of the API, detailed here.