Use AngularJS in Node applications.
AngularJS is a framework for building single-page applications that run in a web browser. To make use of the full power of AngularJS it has to be run in a browser, but a subset of AngularJS can be used within Node applications. This module exists to provide a bridge between NodeJS and AngularJS.
Some possible applications of this capability include:
- Inspect an Angular application's routes on the server so that the server can produce a real 404 response when no route matches.
- Inspect an application's routes and run its
resolve
functions on the server so that the results can be delivered to the client in a single round-trip. - With the help of something like
jsdom
, render static versions of pages to return to robot clients like search engine crawlers that don't run JavaScript code.
This module provides the bridge to AngularJS and enough DOM to allow the AngularJS injector and compiler to work. For most applications further work will be required to override the built-in implementations of various services in order to integrate them with the NodeJS application and in order to prevent behaviors that make sense in the browser but don't make sense in Node.
Unfortunately there are a couple of hurdles to jump before this module can be used.
The first hurdle, that applies to all users, is getting hold of a copy of AngularJS. The stock build from the website should work fine. The author tested this module primarily with version 1.2.0.
Try:
npm install angularcontext
This module doesn't include AngularJS, so you'll have to obtain a copy of it elsewhere and bundle it with your calling application.
You can require
the module in the usual way:
var angularcontext = require('angularcontext');
When AngularJS is running in the browser it has a registry of modules that is global to the application. However, system-wide globals would be a menace in the Node environment so this module provides isolated AngularJS contexts that each contain their own module registry. Instantiate a context to get started:
var context = angularcontext.Context();
Before the context will do anything useful it needs to at least have some version of AngularJS
loaded into it. The runFile
function provides a way to achieve this given a version of Angular
available in a file somewhere:
context.runFile(
'angular.js',
function (result, error) {
if (error) {
console.error(error);
}
else {
// can now use other methods of the context
}
}
);
At the point indicated by a comment in the above example, assuming that the provided AngularJS
module executed successfully, the core ng
module will be available for use and some simple
parts of AngularJS will work without any further tweaking. The injector
method allows us
to obtain an AngularJS injector object that we can use to instantiate AngularJS services as
normal:
var $injector = context.injector(['ng']);
$injector.invoke(
function ($rootScope, $parse) {
var template = $parse('"Hello, " + name');
$rootScope.name = 'Bob';
console.log(template($rootScope)); // logs "Hello, Bob".
}
);
However, to do most useful things it's necessary to register further modules that either provide application-specific functionality or override certain built-in AngularJS services whose normal implementations are inappropriate in the non-browser environment.
One way to do that is to load additional scripts using runFile
. Repeated calls to this method
are somewhat like having a sequence of script
elements in an HTML document, in that they will
all run in the same global scope. The angular
global object is available here, along with
a somewhat-browser-like DOM API.
As a convenience there is also a runFiles
method (note the plural) that takes an array of
filenames and executes them in the order they are provided. Commonly a caller will load the
AngularJS core plus some AngularJS modules and then some application code.
A more powerful method is to directly register a module from the normal NodeJS context. This allows
you to provide a module that makes use of NodeJS functionality and libraries such as jsdom
.
This can be done by calling the module
method, which works just like the equivalent method
on the global angular
object when running in a browser.
var fs = require('fs');
var module = context.module('nodeFilesystem', []);
module.factory(
'nodeFilesystem',
function () {
return fs;
}
);
You can create an injector with any combination of normal AngularJS modules and NodeJS-flavored modules:
var injector = context.injector(['ng', 'nodeFilesystem']);
If you provide a module that overrides service names declared in ng
, be sure to list it
after the 'ng'
entry in the module list, since AngularJS uses a 'last declaration wins'
rule for resolving conflicts.
When you're done with your AngularJS context you must be sure to call dispose
on it to avoid
a memory leak:
context.dispose();
After you've called dispose
you should not call any further methods on the context
object. The
behavior when calling methods on a disposed context is undefined and may even result in crashing
the entire node process.
Development environment setup is pretty standard: just clone the git repo and run npm install
within it.
Contributions are welcome but are assumed to be licensed under the MIT license, as this package itself is. Please be sure to run the unit tests (such that they are) and the linter on your modified version, and then open a pull request.
npm test
npm run-script lint
The project's code style is represented by the .jshintrc
file but please also attempt to remain
consistent with the prevailing style even if particular details aren't tested for by the linter.