How to run Angular 2 in Production Today
Recently there where announcements that made clear that the Angular 2 final release is just around the corner. This might be as early as mid-May during Google IO !
But thats still several months into the future, and there are many projects that will start between now and then. A question that gets asked a lot these days is: should I use Angular 2 or just stick to Angular 1?
Angular 2 is currently in Beta, and being in Beta means the following according to the official announcement:
Beta means we’re now confident that most developers can be successful building large applications using Angular 2
This was about 3 months ago. So the answer is yes, we should definitely use Angular 2 for projects starting today. But how?
Lets answer this and several other questions while going through the following topics:
- A strategy for putting an Angular 2 app in production
- choosing a package manager and a module loader
- Is the bundle size a real concern?
- The current state of the angular-cli
- Choosing a lightweight toolchain while waiting for angular-cli
- How to use Angular 2 with a minimal build
- How to handle HTML templates
- How to handle CSS
- How to handle internationalization
- Conclusions
Current status of the Angular 2 composing parts
The Angular 2 core functionality which includes Components, Directives, Forms, Pipes, Dependency Injection and HTTP has been stable for several months and ready to go. The router is expected to have some API changes to improve developer ergonomics, but the main functionality is already available.
There are still some hoops to jump through, but those are the exception and not the rule.
So what is preventing us from using Angular 2 in production? There are several perceived reasons, one of them could be that the tooling around it is a work in progress.
The current state of the angular-cli
On the long term, one of the great advantages of Angular 2 is that we will have an opinionated way to setup and build a project, via the Angular Command Line Interface (CLI).
This tool will provide us with a way to scaffold projects and a standard build pipeline. The build will internally be based on brocolli and the CLI itself is based on the popular ember-cli.
But most importantly, we will have a build system officially endorsed by the Angular 2 team that will take us from development to production.
The problem is that the angular-cli although already working and being used to build the Angular 2 Material widget library, is currently according to the the docs:
…very much still a work in progress. We still have a long way before getting out of our alpha stage.
So its clear that on the long term we want our Angular 2 projects to be built using angular-cli, but that solution is still in its early stages.
Also related to the toolchain, there are more questions regarding how to setup our project.
Is there a recommended package manager and module loader ?
For one, which package manager and module loader should we choose? The Angular team mentioned recently in the Adventures in Angular podcast that they would be standing behind one package manager and one module loader.
Its was not mentioned which ones at that stage, but we can guess via the documentation and the contents of the project’s google drive that:
- Package manager: The package manager is npm, based on the existence of proposal documents for making npm more friendly for frontend development, and the fact that almost everything can be found in npm these days
- Module Loader: both the quick start tutorial and the initial version of angular-cli use SystemJs, this will likelly be the module loader of choice for building Angular 2 apps.
Its important to bear in mind that this does not mean that the framework will not work in other package managers or module loaders, by the contrary all of them will be supported.
Besides the toolchain, there seems to be another main point that is being worked on: the overall bundle size.
Is the bundle size a real concern?
The current 115KB compressed and minified size of the main Angular 2 bundle might not be a concern depending on the type of application you are building:
- are you doing mobile development? If not the bundle size might not be much of a concern. Many pages that we surf everyday on the Internet load way more Javascript than that
- are you building an enterprise application that will run only on a company LAN? You probably have a very fast server connection, making the bundle size even less of a concern
- are you going to production in less than 2 months? By then the size of the bundle issue (if its an issue at all for your project) should have been fixed
- are you building a productivity single page app? One extra second of initial load time might not be an issue. For example users are used to wait over 5 seconds for Gmail to open
Let’s go back to the meaning of Beta, it means we should be able to run an application in production. Given this and all of the above, can we come up with a strategy to do so?
A strategy for running Angular 2 in production in the next few months
One way to do it is to choose a lightweight toolchain that can be put in place quickly but still is sufficient to put an app in production. At the same time we want to make sure that such toolchain can be easily replaced in a couple of months once angular-cli lands.
Once angular-cli is ready, we will then have all the functionality necessary to compile templates in a build step instead of at startup time. With that the compiler part of Angular will not need to be shipped to the browser and the bundle size will be drastically reduced.
What we want to avoid is to make a large investment in a complex toolchain that we already know upfront that will be obsolete in a matter of months.
So let’s start choosing our toolchain and development practices, with an eye towards the present, but keeping it future-proof regarding a future migration to angular-cli.
Package Manager and Module Loader
Lets start with the simplest choices: its better to use npm as our package manager and SystemJs as module loader, as that seems to be the path of least friction.
Note that other package managers and module loaders would also work, but this is the choice that will likely be easier to migrate to angular-cli, and its the combination that angular-cli currently uses.
How to load Angular 2
For loading Angular in the app itself, a very straightforward way to do so is to simply use a CDN to get your scripts loaded. This gives the following immediate benefits:
- it allows the development to start right now
- it makes for a simpler initial build
- its easy to go back later once angular-cli is available and refactor this
- its actually a viable option to use in production (for many applications) as we will see next
Where to load Angular from
The Cloudflare sponsored Content Delivery Network CdnJs serves for free most scripts needed to run Angular 2, as well as many others. In fact all script needed by Angular are available there except:
- the beta version of Rxjs
- The IE11 compatibility shims (soon to be included in the main bundle)
But those can be taken as well from the npm CDN. Lets now measure some load times. Lets take for example the following script tags:
<script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/systemjs/0.19.24/system.js"></script> <script src="https://npmcdn.com/rxjs@5.0.0-beta.2/bundles/Rx.js"></script> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular.js/2.0.0-beta.9/angular2-polyfills.min.js"></script> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular.js/2.0.0-beta.9/angular2.min.js"></script> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular.js/2.0.0-beta.9/http.min.js"></script> <script src="https://www.webcodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular.js/2.0.0-beta.9/router.min.js"></script> <script src="https://npmcdn.com/angular2@2.0.0-beta.9/es6/dev/src/testing/shims_for_IE.js"></script>
As we can see, everything is being loaded from CdnJs, a couple of scripts, and soon everything will be available in CdnJs. We could load everything from npm CDN as well, but sometimes the load times from the npm CDN are much longer, so its better to use Cloudflare as much as possible, like we are doing here.
You can easily get 300ms of total script load time for all the scripts needed to run Angular 2, including RxJs. This is with the browser cache turned off:
These are pretty acceptable timings ! We can actually run many productivity or enterprise desktop-like apps using this setup, with no problem at all.
But what about other things, like how should we handle our HTML or CSS in a way that its future proof and easy to migrate to angular-cli?
How to handle HTML templates
The main concern about HTML templates is that we want to make sure that we won’t have to load them in separate HTTP requests in production, as this really slows down a lot the application startup.
In Angular 1 we would use a build step to pre-populate the template cache using plugins like gulp-ng-html2js.
A feature will be available soon called Template Transforms (read more in this talk), that will allow to build an ecosystem of tooling around templates. But right now, we have two ways to include templates in a component:
- inline templates: This consists on adding templates as inline strings directly in the component code. It has the advantage that at runtime there is no extra HTTP request, but this method might not scale well for large templates
- external templates: Templates can also be placed in external html files. This is cleaner and scales better specially in a team with designers and several developers editing the same code. But this will result in an extra HTTP request per template
As we can see none of the two solutions is ideal. Lets see if we can come up with a better way, but first let’s see how templates will be handled in the future.
The solution for importing templates in the future
What we need is a mechanism to import a file in Typescript as a string. This already exists in SystemJs itself, but we are only using it to load scripts in the browser and not during Typescript compilation. It also exists in Webpack, via the text loader.
For out setup the ideal would be something like this:
import htmlTemplate: string from "template.html";
And then we could use the template as a variable and pass it on to the component:
@Component({ selector: 'your-component', template: htmlTemplate }) export class YourComponent { }
This functionality will be available in future versions of Typescript, see for example this Github issue. So there is no indication that importing templates will be handled by angular-cli, this will simply be a new feature language of Typescript (although angular-cli might provide an alternative, its currently not clear).
But what can we do while this is not available?
A way of handling HTML templates
If the template of the component is small, the option remains to keep it inline. If the template gets too big, its possible to externalize it in a separate Typescript file that exports a single string. In practice this looks like this:
Notice that the syntax highlighting and the autocompletion is still available even inside embedded strings. This is automatically the case in Webstorm. If by some reason Html is not detected, its possible to so manually using Alt + Enter
inside the template.
The template (named test.html.ts
) can then be imported via the following syntax, and simply used as a string:
import {htmlTemplate} from './test.html';
This has the following advantages:
- this does not cause an extra HTTP request
- The template is an external file, this scales better for larger teams and projects
- Most of the IDE Html tooling works correctly in the external file
- this file can be easily migrated to a plain HTML file once Typescript “import file as string” functionlaity is in place
There is another important aspect of how to use the application in production: how to bundle our Javascript?
A simple way to bundle an Angular 2 application
It might seem something trivial, but once we have our plain Javascript files in a build output directory, how do we prepare a bundle? We need to concatenate the files in the right order and this soon proves cumbersome.
Luckily since Typescript 1.8 there is a simple way: we can configure the compiler to output the compilation result to a single file instead of separate files. For that, we just need to adapt our tsconfig.json
:
{ "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "es5", "module": "system", "sourceMap": true, "outFile": "bundle.js" } }
Notice the value of the outfile property, its a new compiler feature. With it its possible to generate SystemsJs and AMD bundles (currently CommonJs is not yet supported).
In our case this does not pose an issue, as we are using SystemJs. What we have with this is an output bundle of our whole application containing all the templates embedded inside it and ready to be loaded by SystemJs.
It would be just a matter of adding a simple gulp build to cache bust the file name and inject it on our index.html
.
But there are still a couple of loose strings to tie, and the biggest one is: how should we handle CSS in our application?
How to handle CSS
Shadow DOM is not available in all browsers, but we can still import styles at the component level using emulated mode. This will encapsulate the styles to make them affect only the component by prefixing the style with a high specificity selector. Have a look at this example:
.button[_ngcontent-sxu-6] button[_ngcontent-sxu-6], .button[_ngcontent-sxu-6] a[_ngcontent-sxu-6] { text-transform: uppercase; }
The problem is that without the appropriate tooling for generating source maps, this currently cannot be debugged (but it will be so in the future).
A solution for handling CSS
One manageable way to manage a project with a large amount of CSS is to simply include it externally as a link tag, this has always worked and nothing in Angular 2 prevents us for using it.
But to keep it manageable, a solid choice is to use Sass like the Angular 2 Material (the new Angular 2 widget library) project is currently doing, and like the Angular 1 version of the material design widgets is doing too.
The current version of angular-cli has Sass support, and the Angular 2 material alpha is already using angular-cli to handle their Sass/Css.
There is a large chance that if you ever use Angular 2 material in the near future (note: the components can be used one by one), you might have to customize Sass files to adapt the widgets to your application theme.
The most future-proof path of handling Css is to adopt the same pre-processor as the official component suite: Sass.
Using Sass in a reasonable way
Sass is very powerful and there has been a movement (PostCSS) to instead componetize each extension to CSS separately in a plugin architecture. So for example, if we want a feature like Css file imports, variables or nested styles we can add those one by one.
One alternative is to simply use Sass but not abuse its many powerful features. For example, don’t nest styles too deeply just for grouping them under a single component, it creates very long selectors. If inside another component you want to override the styles, you have to create a very long selector, with all the maintenance issues that it brings.
Doing CSS in a structured way
Complementing the use of Sass with a good CSS structuring methodology like Smacss is a good solution. The two are not exclusive, by the contrary its very convenient to implement Smacss using Sass.
Using Sass integrated with Angular 2
Its clear that further down the line it will be possible to use Sass easily with Angular, by simply importing .scss
files in our component.
Then a build step will transform the Sass into CSS and generate the appropriate source mappings that will allow to take advantage of the emulated encapsulated mode in a debuggable way.
But right now, a simple Sass build should get us going, knowing that in the future it will be easy to migrate it to the angular-cli pipeline.
How to handle internationalization
The final release of Angular will contain a working i18n solution, with an extended set of features. And that is the advised solution for i18n in the long term.
We are not sure what the i18n solution will look like, but the translations will very likelly be applied in a build step and multiple versions of the application will be generated.
But if you really need to write an application its better to add translations from the beginning using a version of ng-translate for Angular 2: ng2-translate.
This has the same set of features that ng-translate has, which has been used to translate a very large number of applications in Angular 1. ng2-translate will cover the large majority of the needs of your application.
Conclusions
With a couple of simple methods presented above, its totally possible to start developing an application and also have it running in production today.
By making our choices having in mind what the future will look like, its feasible to setup a build and development methods that suits our immediate needs, but still does not prevent us from easily migrating to angular-cli in the near future.
Reference: | How to run Angular 2 in Production Today from our WCG partner Aleksey Novik at the The JHades Blog blog. |