Prerequisites

Let’s clearly describe our goal.

We have two separated angular SPAs — let’s say that the former is for students and the latter is for headhunters. They are available under https://hunters.com/ and https://students.com/. We already have a third application for serving common assets like CSS and JS.

The snippet above allows us to distinguish our production from our development environment through a particular attribute stored in theenv object which may look as follows:

# development:

env = { ASSETS_HOST: 'http://localhost:8888' } # production:

env = { ASSETS_HOST: 'http://assets.com' }

We manage environmental variables in middleman using dotenv gem and in brunch.io using jsenv.

Use case

Not only we need common JavaScripts and Stylesheets, but mutual HTML templates as well. We have to extract reusable partials out of two applications and store them on the assets server.

The code

We created a simple decorator for $templateCache that wraps get and put functions, tries to fetch templates from our local cache and return them if exist. Otherwise it performs an http request to our assets server and fetches the result that is compiled and placed in its own cache.

Why does it work?

In brunch.io we use an excellent plugin jade-angularjs-brunch. It compiles all HTML templates into one javascript file that represents angular module called partials.

Note that these are regular JS strings containing HTML code. That ensures our templates to be accessible in $templateCache by particular paths.

Thanks to that solution, we have pre-filled $templateCache, so $http.get is performed only when necessary (when requested templates are missing, which means they should be served by assets application).

Another approach

With middleman we had to figure out a quite different solution. Although we have application-specific templates, they are not compiled on the very beginning and $templateCache is empty.

So every request like <ng-include=” ‘partials/template.html’ ”> will hit the assets application, because nothing exists in our own cache yet. In further requests it will fill cache with fetched templates, but not with these stored in middleman-based application.

We need to download and compile the templates from the remote server immediately, and don’t ask for them over http (except that first time) to give the possibility of using application’s templates as well. Instead of using a decorator, as we’ve done before, we can just leverage run function, right?

Problems & UI-Router solution

We encountered some obstacles, which are worth describing here. $http.get in run function loads assets asynchronously. This means that templates sometimes are compiled after the application starts. This results in parts of the application that required shared templates were just missing and didn’t exist in DOM at all.

UI router comes with the solution

We strongly use UI router in our application so we decided to use it for fetching external dependencies. In our root state we resolve partials loading, which lets us to wait for the required templates.

Now we have template cache filled before we start building angular DOM.

Assets app

We use there middleman-angular-templates gem which joins our templates to one HTML file, which can be complied into cache. Just by including

activate :angular_templates

in config.rb we get one html file with angular partials that are ready to compile and require.

The result may looks like as follows:

HTML prepared as above can be directly compiled into angular $templateCache and particular partials will be accessible by corresponding id of each script.

Testing

Although we trust our code, we created tests for it to be sure it works as intended. For the tests we use Jasmine. We created two test cases:

fetching templates from $templateCache

resolving partials from remote url

We also want to test if AssetsPartialsLoader fetches templates via $http.get and compiles them into template cache.

Now we are sure that everything does what it should so we can deploy our solution to production.

Summary

We came a long way to extract common code and to separate two SPAs that can share reusable templates. It was definitely worth it, because with one-time task we achieved a solution that can be easily implemented in any project. We recommend to leverage our work and try to do the same in your applications. Ultimately we all want to divide every monoliths into small, simple micro-applications, don’t we?