Skip to content

Commit 36a5215

Browse files
feat(plugin): Create plugin API
Closes #7
1 parent 5c5f7eb commit 36a5215

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

src/interface.ts

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
// Need to import or export at least one concrete something
1313
import {noop} from "./common/common";
14+
import {UIRouter} from "./router";
1415

1516
/**
1617
* An interface for getting values from dependency injection.
@@ -81,3 +82,7 @@ export interface UIInjector {
8182
getNative(token: any): any;
8283
getNative<T>(token: any): T;
8384
}
85+
86+
export abstract class UIRouterPlugin {
87+
abstract name(): string;
88+
}

src/router.ts

+62-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ViewService} from "./view/view";
77
import {StateRegistry} from "./state/stateRegistry";
88
import {StateService} from "./state/stateService";
99
import {UIRouterGlobals, Globals} from "./globals";
10+
import {UIRouterPlugin} from "./interface";
1011

1112
/**
1213
* The master class used to instantiate an instance of UI-Router.
@@ -41,5 +42,65 @@ export class UIRouter {
4142
this.globals.$current = this.stateRegistry.root();
4243
this.globals.current = this.globals.$current.self;
4344
}
44-
}
4545

46+
private _plugins: { [key: string]: UIRouterPlugin } = {};
47+
48+
/**
49+
* Adds a plugin to UI-Router
50+
*
51+
* This method adds a UI-Router Plugin.
52+
* A plugin can enhance or change UI-Router behavior using any public API.
53+
*
54+
* #### Example:
55+
* ```js
56+
* import { MyCoolPlugin } from "ui-router-cool-plugin";
57+
*
58+
* var plugin = router.addPlugin(MyCoolPlugin);
59+
* ```
60+
*
61+
* ### Plugin authoring
62+
*
63+
* A plugin is simply a class (or constructor function) which accepts a [[UIRouter]] instance and (optionally) an options object.
64+
*
65+
* The plugin can implement its functionality using any of the public APIs of [[UIRouter]].
66+
* For example, it may configure router options or add a Transition Hook.
67+
*
68+
* The plugin can then be published as a separate module.
69+
*
70+
* #### Example:
71+
* ```js
72+
* export class MyAuthPlugin {
73+
* constructor(router: UIRouter, options: any) {
74+
* let $transitions = router.transitionService;
75+
* let $state = router.stateService;
76+
*
77+
* let authCriteria = {
78+
* to: (state) => state.data && state.data.requiresAuth
79+
* };
80+
*
81+
* function authHook(transition: Transition) {
82+
* let authService = transition.injector().get('AuthService');
83+
* if (!authService.isAuthenticated()) {
84+
* return $state.target('login');
85+
* }
86+
* }
87+
*
88+
* $transitions.onStart(authCriteria, authHook);
89+
* }
90+
* }
91+
* ```
92+
*
93+
* @param PluginClass a UI-Router Plugin class (or constructor function).
94+
* @param options options to pass to the plugin
95+
* @returns {T}
96+
*/
97+
addPlugin<T extends UIRouterPlugin>(PluginClass: { new(router: UIRouter, options?: any): T }, options: any = {}): T {
98+
let pluginInstance = new PluginClass(this, options);
99+
var pluginName = pluginInstance.name();
100+
return this._plugins[pluginName] = pluginInstance;
101+
}
102+
103+
getPlugin(pluginName: string): UIRouterPlugin {
104+
return this._plugins[pluginName];
105+
}
106+
}

test/pluginSpec.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { UIRouter, TransitionService, StateService } from "../src/index";
2+
import "../src/justjs";
3+
import { StateRegistry } from "../src/state/stateRegistry";
4+
import { UrlRouter } from "../src/url/urlRouter";
5+
import {UIRouterPlugin} from "../src/interface";
6+
7+
describe('plugin api', function () {
8+
let router: UIRouter;
9+
let $registry: StateRegistry;
10+
let $transitions: TransitionService;
11+
let $state: StateService;
12+
let $urlRouter: UrlRouter;
13+
14+
beforeEach(() => {
15+
router = new UIRouter();
16+
$registry = router.stateRegistry;
17+
$state = router.stateService;
18+
$transitions = router.transitionService;
19+
$urlRouter = router.urlRouter;
20+
router.stateRegistry.stateQueue.autoFlush($state);
21+
});
22+
23+
class FancyPlugin extends UIRouterPlugin {
24+
constructor(public router: UIRouter) {
25+
super();
26+
27+
}
28+
name() { return "fancyplugin" }
29+
}
30+
31+
describe('initialization', () => {
32+
it('should return an instance of the plugin', () => {
33+
let plugin = router.addPlugin(FancyPlugin);
34+
expect(plugin instanceof FancyPlugin).toBeTruthy();
35+
});
36+
37+
it('should pass the router instance to the plugin constructor', () => {
38+
let pluginRouterInstance = undefined;
39+
function Plugin(router) {
40+
pluginRouterInstance = router;
41+
this.name = () => "plugin";
42+
}
43+
44+
router.addPlugin(<any> Plugin);
45+
expect(pluginRouterInstance).toBe(router);
46+
});
47+
48+
it('should throw if the plugin constructor returns an object without name() getter', () => {
49+
function Plugin(router) {
50+
}
51+
52+
expect(() => router.addPlugin(<any> Plugin)).toThrow()
53+
});
54+
});
55+
56+
describe('getPlugin', () => {
57+
it('should return the plugin instance', () => {
58+
router.addPlugin(FancyPlugin);
59+
let plugin = router.getPlugin('fancyplugin');
60+
expect(plugin instanceof FancyPlugin).toBeTruthy();
61+
});
62+
})
63+
});

0 commit comments

Comments
 (0)