In my recent exploits I put KnockOut JS (KO) through a real worldy example. I tried to assemble everything I believe an enterprise app is going to need, to get started. The app is a trade entry screen which allows the user to enter a trade and shows updates in a grid/blotter. As rows are clicked they show up on the event viewer as events.
Web client stack
Single Page App (SPA) which communicates to the server exclusively via a REST API.
- Knockout JS
- Curl JS as the AMD loader. Provides client side Dependency Injection too.
- Postal JS for intra app communication
- Knockout-amd-helpers plugin – to load KO templates and componentize the UI
- jQuery UI for the date widget. Bound using Knockout-jQueryUI bindings
- Kendo UI for the Grid widget. Bound using Knockout-KendoUI bindings
- Atmosphere-jQuery for consuming updates over web sockets with fallback
- Jasmine for unit tests. Integrated with Curl.
Server side stack
- Spring MVC. Jackson for JSON serialization.
- Atmosphere library for data pushes
- Selenium for acceptance tests. Selenide to make life easier.
- JRebel for auto reloading Java code.
How to run the sample
You will need to have Maven installed. Everything else will be pulled down.
git clone https://github.com/bsandhu/myserver.git
Once the server starts, navigate to http://localhost:8080/myserver/resources/js/app/views/App.html#
Web client code walkthrough
The code is available on Github in all its glory.
Since this is a Java based app, the directory layout follows Maven. Maven support for JS is a bit all over the place. I should probably follow the JS layout specified by this mojo.
The client is bootstrapped by Curl. Curl is a JS module (lib) loader which can asynchronously load JS in the browser. Additionally it loads them in the order of dependencies. Once these dependencies are declared they can be referenced by the key name throughout the app. Makes it easy to upgrade and inject the JS libs. Here is the curl config. I tried using RequireJS too but found Curl to be easier and more intuitive.
App.html is the entry point to the application.
- This is where all the KO templates and View Models are bound. Each template has a backing VM and represents a self contained UI component. Note that the View Models (VM) are loaded via the knockout-amd-plugin.
- The Kendo splitter component is bound to KO using the KO-kendo binding lib.
In EventsView.html you can see nested UI components/templates (UpdatesView). Each UI component is unaware of any other components. It publishes its own state changes and listens for events from the outside world – via Postal JS, a client side messaging solution. The following snippet EventsViewModel.js is:
- Defined as an AMD component (define fn) so it can be managed by Curl.
- Bound to the tabular view (top right in the screenshot above)
- Listens for selection events on the trade blotter channel. (In Postal JS you can listen selectively to channels, so it doesn’t have to broadcast each message to each listener)
- Stores the msg in HTML 5 local storage usage.
Factoring out common services
It helps to separate common services into their own modules and inject these into the VMs as needed. This promotes modularity and testability. This is shown in the TicketViewModel.js where TradeService is injected. The TradeService module can potentially be stubbed out for testing (Spies in Jsamine).
Testing the View Models
I plan to cover View Models/services with Jasmine based unit tests. There is a little bump in the road – Jasmine now needs a reference to a VM which is usually put together by Curl. Hence, Curl needs to do the same for the test context. I had to modify the Jasmine spec runner a little bit to achieve this. This solution requires you to specify the Spec under test as a dependency. This could be improved.
In the next post I will walkthroug the Atmosphere implementation on the client and the server and its integration with Spring MVC.