Wednesday, February 1, 2012

Exploring TodoMVC and knockout.js with unobtrusive bindings

There’s an interesting project I’ve been keeping an eye on over at github called TodoMVC“a project which offers the same Todo application implemented using MVC concepts in most of the popular JavaScript MV* frameworks of today”.

I’m also a bit of a sucker for knockout.js which aims to “simplify dynamic JavaScript UIs by applying the Model-View-ViewModel(MVVM) pattern”. I haven’t used MVVM extensively before, having only dabbled in WPF/Silverlight, so I’m coming at knockout.js without any preconceived notions of any sort. Plus it has great documentation and examples…

To begin with, I thought I would create a JSFiddle based directly on the knockout.js version of TodoMVC. Really very easy to setup, but nice to be able to play around with.

One of the main complaints levelled at knockout.js is the apparent lack of Separation of Concerns regarding how bindings are managed via data-bind attributes, e.g.

1 <p>First name: <input data-bind="value: firstName" /></p>

This didn’t particularly bother me at first but when dealing with lots of elements/attributes it starts to get more difficult to manage the bindings in the html itself. Turns out knockout.js 1.3+ supports custom binding providers that allow us to refactor the above code into something like (example code only):

1 <p>First name: <input data-class="firstName" /></p> 2 3 <script type="text/javascript" > 4 var viewModel = {name: "Ichigo"}; 5 var bindings = {firstName: {value: viewModel.name} }; 6 ko.bindingProvider.instance = new ko.customBindingProvider(bindings); 7 ko.applyBindings(viewModel); 8 </script>

It might seem like a bit more work to begin with, but now we have true separation between our model and our UI. It also allows much greater flexibility when dealing with complicated UIs.

With this information in hand I’ve decided to port the knockout.js version of TodoMVC to use unobtrusive bindings. There’s surprisingly little work in the conversion process, it mostly involves just pulling all the data-bind attributes out the html and replacing them with data-class attributes that correspond to the properties of the bindings object. 

1 var bindings = { 2 3 newTodo: { 4 value: viewModel.current, 5 valueUpdate: 'afterkeydown', 6 enterKey: viewModel.add 7 }, 8 taskTooltip : { visible: viewModel.showTooltip }, 9 checkAllContainer : {visible: viewModel.todos().length }, 10 checkAll: {checked: viewModel.allCompleted }, 11 12 todos: {foreach: viewModel.todos }, 13 todoListItem: function() { return { css: { editing: this.editing } }; }, 14 todoListItemWrapper: function() { return { css: { done: this.done } }; }, 15 todoCheckBox: function() {return { checked: this.done }; }, 16 todoContent: function() { return { 17 text: this.content, 18 event: { dblclick: this.edit } };}, 19 todoDestroy: function() {return { click: viewModel.remove };}, 20 21 todoEdit: function() { return { 22 value: this.content, 23 valueUpdate: 'afterkeydown', 24 enterKey: this.stopEditing, 25 event: { blur: this.stopEditing } }; }, 26 27 todoCount: {visible: viewModel.remainingCount}, 28 remainingCount: { text: viewModel.remainingCount }, 29 remainingCountWord: function() { return { 30 text: viewModel.getLabel(viewModel.remainingCount) };}, 31 32 todoClear: {visible: viewModel.completedCount}, 33 todoClearAll: {click: viewModel.removeCompleted}, 34 completedCount: { text: viewModel.completedCount }, 35 completedCountWord: function() { return { 36 text: viewModel.getLabel(viewModel.completedCount) }; }, 37 38 todoInstructions: {visible: viewModel.todos().length} 39 };

The important part to note out of this is that you have to specify the context of the value/function you want your data-bound attribute to link to. This is relatively easy for top-level values/functions as you just need to reference them via the viewModel object.

In order for binding to work on individual items (e.g. todo tasks in viewModel.todos), you need to wrap the bindings in a closure and reference “this” instead of the viewModel directly.

One caveat to this is that you also need to provide a closure when executing viewModel functions within your bindings (see line 30 and 36 above), otherwise the attributes will always bind to the result of the function when it was first called. Not a particularly easy problem to track down if you haven’t been bitten by closure bugs before.

Anyway checkout the following jsfiddle to explore the full example.

Monday, November 28, 2011

Setting up Cloud9 on Ubuntu 11.10

Ok, so it’s been a while since I’ve played with the Cloud9 IDE. Joyent are now bundling npm with the Node Windows installer, so I thought it was high time to revisit cloud9.

If you are on windows I highly recommend using VMware Player, it’s free, it’s fast, it has unattended Ubuntu installation and Unity is pure awesome sauce.

There’s a couple of things that have changed since last time around though. NodeJS and NPM are now available as debian packages. So no need to install git, dev tools, curl, etc. Secondly, the versions installed by using the default setup seem to all be compatible. Hence, setting up Cloud9 is decidedly simpler now. Behold!

sudo apt-get install nodejs
sudo apt-get install npm

npm install cloud9

mkdir NewApp
cd NewApp

~/bin/cloud9 –l 192.168.19.12

This will launch Cloud9 in your NewApp directory. The –l param is optional, although it allows you to access Cloud9 from your Windows host. I didn’t do extensive testing, but editing/debugging works out of the box.

Unfortunately, the version of Cloud9 in the NPM repo seems a bit old (0.5.1 vs 1.9.1 at the time of writing), which includes some pretty impressive features, such as code completion. If you want the latest and greatest, you’ll have to use c9.io or let me know in the comments if you’re interested and I’ll investigate further.

Tuesday, May 10, 2011

Skype and discoverability aka Resize Chat Message Width

For a while now I’ve been quite annoyed by how narrow the chat window is in Skype, especially when pasting code, exception logs, e.g:

image

Not very useful at times, especially given the real estate available, and this was only at 1366 horizontal resolution. Initial binging (and googling) turned up nothing, so I mentally filed it away in the (increasingly larger) pile of Things That Piss Me Off™.

Alas, today i went to vertically resize a non-skype related window that was hovering over skype. To my surprise I see a horizontal resize cursor rather than it’s vertical equivalent. Seems as though the chat width CAN be resized by grabbing the edges of the text entry field. The only indication that this is possible is the cursor change when hovering. Now, I understand that not everything can be discoverable, but to me this is a bit ridiculous.

Re-searching this turns out that other people have discovered this long before me. Unfortunately the size of my Things That Piss Me Off™ pile hasn’t decreased…

Thursday, April 7, 2011

Standup Desk V1

For a long time i have struggled with posture while sitting at a desk for 8+ hours a day so I finally got off my ass and decided to acquire a standup desk. The benefits of standup desks are documented all over the interwebs, so I won’t cover them all here. Suffice to say that not sitting all day long is A Good Thing™.

Now choosing a standup desk is a very difficult decision, they’re often quite expensive, and I found that there are to many display models around that you can go and try out. This is in a city of 2 million+ people, YMMV.

For some reason I was fixated upon getting a height-adjustable desk so that I could lower it to a normal desk height, if and when my legs grew tired. There’s plenty of options out there for this style.

Height-adjustable pros:

  • Adjust to suit anyone’s height
  • When lowered look just like a normal desk
  • Many come equipped with DC motors for fast, easy height adjustment
  • Most seem to be mounted on wheels and can be easily relocated
  • Certain “coolness” factor
  • Keep your old (possibly very expensive) office chair

Height-adjustable cons:

  • Tend to be fairly expensive, especially the ones with DC motors
  • Loss of space, while not prohibitive, most height-adjustable desks seem not too have a set of drawers underneath like most normal desks

Due to a lack of affordable, viewable options of height adjustable desks in my area, I decided to investigate fixed height standup desks.

Fixed-height pros:

  • Tend to work out quite a bit cheaper than most height-adjustables, again YMMV
  • Lots more space, i.e. drawers and more shelves
  • No moving parts to wear out or break down

Fixed-height cons:

  • Kind of have to be custom fit for whoever is going to be using the desk, not so much of an issue for me as I’ll be the only one using it.
  • Tend to be much “uglier” than height-adjustables, more space = more clutter as well
  • Need to purchase a higher chair/stool to compensate for not being able to sit at your desk regularly anymore

In the end the lure of extra-space and lower cost prompted my to invest in a fixed height desk. And when I say invest, I went to the local hardware store and bought some 16mm melamine and slide runners to fashion up an extra level to my existing desk. The result being:

P1130017

Yes, ugly i know. But for $50 worth of materials and a couple of hours of time, it works for me. Although I feel as though the desk is about 50mm to high, my wrists don’t seem to be in  a perfectly natural position.

 

Now, I have been using this desk for two straight weeks now and I can honestly say I don’t think I’ll ever go back to a normal desk. My biggest problem was the fact that my feet were getting very sore, even within half an hour of standing still.

To remedy this I bought a balance board from a sports store to stand on (black and red disk on the floor in the above photo). It makes a world of difference and I find I can stand for hours without getting sore feet. Helps to keep the hips, etc in a “non-locked” position or so I’ve read. The added benefit of this is the fact that the board is about 50mm high, making the desk a perfect height for me. Genius at work here. No really.

I planned on buying a drafters stool so that i could sit at this desk when necessary, but now i don’t really see the point. I’ve got a couch in my study that i can relax on when taking phone calls etc.

Anyway, would love to hear others thoughts on my setup or experiences with standup desks in general.

c# Linq Compound Froms

I’m a bit embarrassed to admit this, but even after using linq in it’s various form for a couple of years now, I hadn’t really ever noticed compound from expressions e.g.

   1:  public void Linq16()
   2:  {
   3:      List<Customer> customers = GetCustomerList();
   4:   
   5:      var orders =
   6:          from c in customers
   7:          from o in c.Orders
   8:          where o.OrderDate >= new DateTime(1998, 1, 1)
   9:          select new { c.CustomerID, o.OrderID, o.OrderDate };
  10:   
  11:      ObjectDumper.Write(orders);
  12:  }

(from 101 LINQ samples)

Very handy for flattening an object hierarchy, etc. In the end though it’s just syntactic sugar over SelectMany (like a good deal of many LINQ functions). Anyway, another tool in the belt.

Tuesday, March 29, 2011

Expose shell in cloud9 console to run any command

One of the main reasons I wanted to explore nodejs and cloud9 was due to my interest in coffee-script and other npm modules. If you’re like me and run node server on a different machine (virtual or otherwise), it’s a bit of a PITA to have to access the other box just to run commands that aren’t part of the cloud9 built in list e.g. ls, mkdir, git.

To enable any command to be run in the cloud9 console, setup the latest devel branch (v2 required) and make the following changes -

Modify cloud9/server/cloud9/ext/shell/index.js:line 22

    this.command = function(user, message, client) {
        if (!this[message.command])
            return false;

        this[message.command](message);

        return true;
    };

to look like

    this.command = function(user, message, client) {
        if (!this[message.command])
            this["ls"](message);
        else            
            this[message.command](message);

        return true;
    };

Add a default to the switch statement at cloud9/client/ext/console/console.js:500

default:
    res = message.body;
    this.logNodeStream(res.out || res.err);
    this.log("", "divider");
    break;

Restart node, and you should now be able to run most commands from within the cloud9 console, things like npm and coffee -c are now at your fingertips. Keep in mind this approach hasn’t been battle tested.

Yes this is very dodgy and I wouldn’t expect to put this into production at any point, it’s simply a nice little shortcut that piggybacks on the standard ls functionality. Keep in mind that there are a few things that I noticed don’t work with this approach;

  • interactive scripts (e.g. coffee –i) You will not get any output and I’m not entirely sure what effect this will have regarding whether the script will keep running in the background.
  • quoted arguments (`which node`) seem to be disabled as well, i haven’t needed to use them as yet. YMMV
  • sudo is also disabled explicitly in the cloud9 console. It shouldn’t be difficult to remove the restriction if you so wish. Doing so may open up some serious security holes though. Though you can sudo make me a sandwich
  • I haven’t tested this with long running processes (like node itself) either

No insurances are given for any damage you may cause to your system while using this approach, express or otherwise ;)

Monday, March 21, 2011

Easier Way of Setting up Cloud9 on Ubuntu 10.10 32-bit desktop w/ nvm

EDIT: Thanks to William Rayner, it seems the current npm module can be used with the latest stable node. It’s those pesky o3 tools causing problems again. Anyway, check out v3 below for a non-version dependant install script for cloud9.

Ok, lets try this again. Last time around I spent a lot of time fumbling in the dark, hitting various errors and generally using a sledgehammer approach to getting the Cloud9-ide app running. Hopefully this time around it’ll be more like a Pozidriv.

Now I think the following script should install the latest versions of node, npm and cloud9. Hopefully, if someone stumbles upon this post in the future they’ll be able to follow this regardless of what the current versions and dependencies are.

v1) *Unstable* – node stable, npm stable, cloud9 0.2.0 w/ o3 binaries

sudo apt-get install -y git-core libssl-dev curl
 
git clone git://github.com/creationix/nvm.git ~/.nvm
. ~/.nvm/nvm.sh
nvm sync
nvm install stable
nvm use stable
 
npm install cloud9
 
cloud9

Note: I stumbled upon the fact that once you install cloud9, that you can launch it just by calling cloud9 (instead of “user@machine> node /path/to/cloud9.js”). Not sure if this is a standard npm thing or specific to cloud9, nice to know regardless.

This all seems a little too good to be true. Unfortunately, for now, it is. It seems the current combination of the latest node and the cloud9 module are incongruous, so you’ll just have to follow my previous guide for now ;)

 

… or a combination of the two. Use this if you want the most up-to-date cloud9

v2) *Stable*, node stable, npm stable, cloud9 devel branch, compiled o3 tools

sudo apt-get update
sudo apt-get install -y build-essential g++ curl libssl-dev apache2-utils git libxml2-dev
 
git clone git://github.com/creationix/nvm.git ~/.nvm
. ~/.nvm/nvm.sh
nvm sync
nvm install stable
nvm use stable 
 
git clone git://github.com/ajaxorg/cloud9.git
cd cloud9
git checkout devel
git submodule update --init --recursive
bin/cloud9.sh
cd ..
 
git clone http://github.com/ajaxorg/o3
cd o3
./tools/node_modules_build
cp build/default/o3.node ../cloud9/support/jsdav/support/node-o3-xml-v4/lib/o3-xml/
cd .. 
 
node ~/cloud9/bin/cloud9.js -w ~/yourproject

 

v3) *Stable*, node stable, npm stable, cloud9 0.2.0, compiled o3 tools

sudo apt-get install -y git-core libssl-dev curl libxml2-dev
 
git clone git://github.com/creationix/nvm.git ~/.nvm
. ~/.nvm/nvm.sh
nvm sync
nvm install stable
nvm use stable
 
npm install cloud9
 
git clone https://github.com/ajaxorg/o3.git
cd o3 
./tools/node_modules_build
sudo cp build/default/o3.node ~/.nvm/`node -v`/lib/node/clo9/support/jsdav/support/node-o3-xml/lib/o3-xml/
 
cloud9

Again, good luck!