diff --git a/lib/component-renderer.ts b/lib/component-renderer.ts index 3b7e3f7..0171c46 100644 --- a/lib/component-renderer.ts +++ b/lib/component-renderer.ts @@ -21,12 +21,20 @@ import { fromCallback, compose } from './util'; import { Route } from './route'; import { Middleware, identity, provideMiddlewareForToken } from './middleware'; -export const RENDER_MIDDLEWARE = new OpaqueToken( - '@ngrx/router Render Middleware' +export const PRE_RENDER_MIDDLEWARE = new OpaqueToken( + '@ngrx/router Pre Render Middleware' ); -export const useRenderMiddleware = provideMiddlewareForToken( - RENDER_MIDDLEWARE +export const POST_RENDER_MIDDLEWARE = new OpaqueToken( + '@ngrx/router Post Render Middleware' +); + +export const usePreRenderMiddleware = provideMiddlewareForToken( + PRE_RENDER_MIDDLEWARE +); + +export const usePostRenderMiddleware = provideMiddlewareForToken( + POST_RENDER_MIDDLEWARE ); export interface RenderInstruction { @@ -44,7 +52,10 @@ export interface ResolvedInstruction { @Injectable() export class ComponentRenderer { - constructor(@Inject(RENDER_MIDDLEWARE) private _middleware: Middleware[]) { } + constructor( + @Inject(PRE_RENDER_MIDDLEWARE) private _preMiddleware: Middleware[], + @Inject(POST_RENDER_MIDDLEWARE) private _postMiddleware: Middleware[] + ) { } render( route: Route, @@ -57,14 +68,15 @@ export class ComponentRenderer { .map(component => { return { component, injector, providers }; }) - .let(compose(...this._middleware)) + .let(compose(...this._preMiddleware)) .map(instruction => { const providers = Injector.resolve(instruction.providers); const component = instruction.component; return { providers, component, ref, dcl } }) - .mergeMap(instruction => this.renderComponent(instruction)); + .mergeMap(instruction => this.renderComponent(instruction)) + .let(compose(...this._postMiddleware)); } renderComponent({ component, providers, ref, dcl }: ResolvedInstruction) { @@ -85,6 +97,7 @@ export class ComponentRenderer { } export const COMPONENT_RENDERER_PROVIDERS = [ - useRenderMiddleware(identity), + usePreRenderMiddleware(identity), + usePostRenderMiddleware(identity), new Provider(ComponentRenderer, { useClass: ComponentRenderer }) ]; diff --git a/lib/index.ts b/lib/index.ts index 1e127b6..dfeb792 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -32,6 +32,6 @@ export { LocationChange, Location } from './location'; export { Middleware, createMiddleware } from './middleware'; export { RouteParams } from './route-params'; export { useLocationMiddleware, useRouteSetMiddleware, RouteSet, NextRoute } from './route-set'; -export { useRenderMiddleware, RenderInstruction } from './component-renderer'; +export { usePreRenderMiddleware, usePostRenderMiddleware, RenderInstruction } from './component-renderer'; export { Routes, Route, IndexRoute } from './route'; export { useTraversalMiddleware } from './match-route'; diff --git a/spec/component-renderer.spec.ts b/spec/component-renderer.spec.ts index 7ca0fa8..139bf97 100644 --- a/spec/component-renderer.spec.ts +++ b/spec/component-renderer.spec.ts @@ -3,7 +3,13 @@ import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import { Injector, provide, DynamicComponentLoader, ElementRef } from 'angular2/core'; import { Location } from '../lib/location'; -import { ComponentRenderer, RENDER_MIDDLEWARE, useRenderMiddleware } from '../lib/component-renderer'; +import { + ComponentRenderer, + PRE_RENDER_MIDDLEWARE, + POST_RENDER_MIDDLEWARE, + usePreRenderMiddleware, + usePostRenderMiddleware +} from '../lib/component-renderer'; import { Route } from '../lib/route'; import { createMiddleware } from '../lib/middleware'; @@ -27,7 +33,8 @@ describe('Component Renderer', function() { beforeEach(() => { injector = Injector.resolveAndCreate([ ComponentRenderer, - provide(RENDER_MIDDLEWARE, { useValue: [] }), + provide(PRE_RENDER_MIDDLEWARE, { useValue: [] }), + provide(POST_RENDER_MIDDLEWARE, { useValue: [] }), provide(DynamicComponentLoader, {useClass: MockDCL}) ]); @@ -60,7 +67,7 @@ describe('Component Renderer', function() { }); }); - describe('Middleware', () => { + describe('Pre Middleware', () => { let route: Route = { component: TestComponent }; let providers = []; let elementRef = {}; @@ -86,7 +93,8 @@ describe('Component Renderer', function() { injector = Injector.resolveAndCreate([ ComponentRenderer, - useRenderMiddleware(renderMiddleware), + usePreRenderMiddleware(renderMiddleware), + provide(POST_RENDER_MIDDLEWARE, {useValue: []}), provide(DynamicComponentLoader, {useClass: MockDCL}) ]); @@ -122,4 +130,53 @@ describe('Component Renderer', function() { }); }); }); + + describe('Post Middleware', () => { + let route: Route = { component: TestComponent }; + let providers = []; + let elementRef = {}; + let middlewareSpy = jasmine.createSpy('middleware'); + let renderMiddleware; + let render; + + beforeEach(() => { + renderMiddleware = createMiddleware(function(instruction$) { + return (componentRef$) => componentRef$.map((componentRef) => { + middlewareSpy(); + + return false; + }); + }, []); + + injector = Injector.resolveAndCreate([ + ComponentRenderer, + usePostRenderMiddleware(renderMiddleware), + provide(PRE_RENDER_MIDDLEWARE, {useValue: []}), + provide(DynamicComponentLoader, {useClass: MockDCL}) + ]); + + renderer = injector.get(ComponentRenderer); + loader = injector.get(DynamicComponentLoader); + }); + + beforeEach(() => { + render = renderer.render(route, injector, elementRef, loader, providers); + }); + + it('should execute after rendering', (done) => { + render.subscribe(() => { + expect(loader.loadNextToLocation).toHaveBeenCalled(); + expect(middlewareSpy).toHaveBeenCalled(); + done(); + }); + }); + + it('can modify the component ref', (done) => { + render.subscribe((componentRef) => { + expect(loader.loadNextToLocation).toHaveBeenCalled(); + expect(componentRef).toEqual(false); + done(); + }); + }); + }); });