How does Angular2 work? Part 2 — bootstrapModule()

Written by MertzAlertz | Published 2016/09/22
Tech Story Tags:

TLDRvia the TL;DR App

If you missed it — check out Part 1 of this series!

Movin’ on up! Now we’re diving in to bootstrapping an application. Since we’re focusing on the basic model given by the ng cli, we’ll take a look at the simple bootstrap contained within application_ref.ts.

In ApplicationRef_ the implementation of bootstrapModule actually just contains a simple call to a private method, _bootstrapModuleWithZone. If you missed my brief overview on zones, you can find it here, but I’ll do a deeper article at some point in this series as well.

First off we notice that now we instantiate a compiler;

const compilerFactory: CompilerFactory =this.injector.get(CompilerFactory);const compiler = compilerFactory.createCompiler(Array.isArray(compilerOptions) ?compilerOptions : [compilerOptions]);

If you’ll recall from Part 1, the compiler that comes with the dynamicBrowserPlatform is the RuntimeCompilerFactory. Looking into the createCompiler method, the code makes an injector, which provides the config that was passed in via the Factory’s constructor, and then gets the Compiler from the injector.

What does the compiler look like? Well, although the COMPILER_PROVIDERS specifically call out many compilers, this line assigns the Compiler we’re looking for. The compiler provided here is the RuntimeCompiler, an internal class of the Angular2 framework that compiles templates and components dynamically for use in the app. RuntimeCompiler is a subclasses Compiler. Remember the RuntimeCompiler, as we’ll come back to it shortly.

Continuing moving through _bootstrapModuleWithZone, the third argument is componentFactoryCallback. The method that calls bootstrapModuleWithZone in _bootstrapModule calls it like this;

this._bootstrapModuleWithZone(moduleType, compilerOptions, null);

in other words, there is no callback.

The method instead returns a call to the compiler, compileModuleAsync, with an argument of the moduleType, which is in our case is our AppModule. That’s right, the first piece of code that we actually supplied (kind of) has entered the equation.

compileModuleAsync, in turn, calls a private method, _compileModuleAndComponents, with the first argument being our AppModule, and the second being a boolean that tells the compiler whether it should perform the compile asynchronously.

The method looks like this,

const componentPromise = this._compileComponents(moduleType,isSync);const ngModuleFactory = this._compileModule(moduleType);

return new SyncAsyncResult(ngModuleFactory,componentPromise.then(() => ngModuleFactory));

Diving into the _compileComponents method, the first line that isn’t a simple declaration is this;

const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);

Where mainModule is the AppModule provided by ng-cli’s boilerplate.

The _metadataResolver comes into the RuntimeCompiler as a private property injected from the Compiler’s constructor. In CompileMetadataResolver, let’s take a look at what happens to get the module metadata, and what the response looks like.

First we resolveForwardRef, which the developers of Angular2 were nice enough to give a plnkr example of. This forward reference allows the resolution of a class that isn’t necessarily inline before the CompileMetadataResolver.

Moving on, the code then checks to see if the moduleType has already been compiled and retrieve it from the cache,

var compileMeta = this._ngModuleCache.get(moduleType);

Since this is our first execution of the code, we assume this is undefined and continue on. The code then attempts to resolve the metadata for the module via _ngModuleResolver, another injected private property.

NgModuleResolver leads us to an actually relatively simple class, simple enough that I can replicate it below;

@Injectable()export class NgModuleResolver {constructor(private _reflector: ReflectorReader = reflector) {}

resolve(type: Type<any>, throwIfNotFound = true): NgModule {const ngModuleMeta: NgModule =this._reflector.annotations(type).find(_isNgModuleMetadata);if (isPresent(ngModuleMeta)) {return ngModuleMeta;} else {if (throwIfNotFound) {throw new Error(`No NgModule metadata found for '${stringify(type)}'.`);}return null;}}}

So this asks the Reflector for information about the specific annotations for our module. Reflector is actually injected and instantiated way back (from part 1) in our platform_core_providers, and the ReflectionCapabilities, which Reflector draws from, are actually just the default implementation found here.

We’re most curious right now about the annotations, so let’s take a look. Before looking at this code, it might be useful to have a look at Paschal Precht’s article about Annotations vs. Decorators. So, our AppModule does in fact have an annotation, and the reflector will look for .annotations or .decorators and return the relevant NgModule annotation.

Stepping back out to this code;

function _isNgModuleMetadata(obj: any): obj is NgModule {return obj instanceof NgModule;}

...

const ngModuleMeta: NgModule = this._reflector.annotations(type).find(_isNgModuleMetadata);

We find the specific annotation that is an instance of NgModule, corresponding to the

@NgModule({...})export class AppModule {

}

From our own code. The contents of the NgModule annotation are now assigned to ngModuleMeta and returned from the NgModuleResolver.

We’re now back here with the successfully resolved meta information provided by the NgModule annotation. I’ll leave it here, as I think this article has gone on long enough. Keep an eye out for Part 3 when we delve into the resolution of the NgModule metadata!

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.

To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!


Published by HackerNoon on 2016/09/22