read

About a year ago, I started building a new product while I was at Digital Reasoning called stackd.io. I evaluated a lot of frameworks with a preference towards small and unopinionated because the intent was to have a very basic user interface for the product, so I didn't need some heavy tool with loads of features. I just wanted something that would provide me with solid DOM data-binding and a lightweight architecture.

I settled on Knockout.

While I was building stackd.io, there were a few things that I felt I could build on top of Knockout that would make my life a lot easier. At that point, I had a choice. I could go out and find a handful of existing tools that provided those enhancements, or I could write a utility myself that did it.

Now, I know the dangers or adding YET. ANOTHER. LIBRARY. into our ecosystem when I could use what already exists, but I took it as an opportunity to see if I could replicate what other frameworks were using to more completely understand what they are doing. Because for me, unless it's a core feature that could be better left to a major library, I see no reason not to try to bring it in-house instead of cluttering up the repo with 5 more minor libraries.

In the end, I learned a lot of lessons and ended up with a library that not only suited my product to a 'T' without introducing any unnecessary bloat, but was also easily modified to make it useful by other people. So I decided to break out the core concepts of what I wrote and made a lightweight utility that could share on Github (with the high likelihood that no one else will ever use it besides me).

The result was galaxy.js.

Here's some of the major things I implemented:

Loading ad-hoc HTML templates

A way to load HTML files with view-specific fragments when needed. So instead of having all the HTML markup authored in one file, I can now write a base template, and then inject child DOM into it only if the user chooses to view that content.

Application routing based on URL hash

A basic implementation of URL hash routing which is set up like this: $galaxy.route('users').to('user.list');. This looks for the URL hash #users and will automatically load and render the Knockout view model with an id of user.list.

Chained functions

Speaking of that, I figured out how to write a simple function that would allow one, or more, chained function calls after it. For example, with the routing function, you can route().to(), or you can add an additional method for a callback: route().to().then(fn). The way I implemented it may very well be naive or inefficient, but it was fun to implement.

Optional chained functions

Using that design pattern, I also created a single function to navigate between views in your application. $galaxy.warp().to('users').engage(); will simply navigate to a new view, but you can add with() on the end to send a data payload $galaxy.warp().to('userdetail').with({user: user}).engage();.

Data stores

You can use galaxy.js to create simple stores of data which can use in a single view, or build your own meta-store mechanism to share stores of data across views. I have plans to expand this functionality, but right now you simply create a store with UserStore = new $galaxy.depot(); to which you can then UserStore.add() arbitrary objects which are exposed via the UserStore.collection() array.

Like I said, I learned a lot along the way and have an even deeper respect for the folks who maintain these larger frameworks because there are a lot more things that have to be accounted for than I ever would have known about had I never done this experiment.

Blog Logo

Steve Brownlee

Head Coach at Nashville Software School. Evolving software development education.


Published