From 55f3d3ddb7088af20aaef82911ce75797377e307 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Wed, 17 Aug 2016 19:06:46 -0500 Subject: [PATCH] feat(urlRouter): Allow a rule to be deleted. feat(urlRouter): Provide the new `rule` created by `.when()` to a callback fn test(urlRouter): convert to typescript --- src/state/stateObject.ts | 2 + src/state/stateQueueManager.ts | 2 +- src/state/stateRegistry.ts | 4 +- src/url/urlMatcher.ts | 10 ++-- src/url/urlRouter.ts | 24 +++++----- .../{urlRouterSpec.js => urlRouterSpec.ts} | 48 +++++++++++++++---- 6 files changed, 61 insertions(+), 29 deletions(-) rename test/ng1/{urlRouterSpec.js => urlRouterSpec.ts} (86%) diff --git a/src/state/stateObject.ts b/src/state/stateObject.ts index 35893547a..cddfc7ccd 100644 --- a/src/state/stateObject.ts +++ b/src/state/stateObject.ts @@ -31,6 +31,8 @@ export class State { public resolvables: Resolvable[]; public resolvePolicy: any; public url: UrlMatcher; + /** @hidden temporary place to put the rule registered with $urlRouter.when() */ + public _urlRule: any; public params: { [key: string]: Param }; public views: { [key: string]: _ViewDeclaration; }; public self: StateDeclaration; diff --git a/src/state/stateQueueManager.ts b/src/state/stateQueueManager.ts index 5ac7dcbbe..59d7a7afa 100644 --- a/src/state/stateQueueManager.ts +++ b/src/state/stateQueueManager.ts @@ -98,6 +98,6 @@ export class StateQueueManager { if ($state.$current.navigable !== state || !equalForKeys($match, $stateParams)) { $state.transitionTo(state, $match, { inherit: true, location: false, source: "url" }); } - }]); + }], (rule) => state._urlRule = rule); } } diff --git a/src/state/stateRegistry.ts b/src/state/stateRegistry.ts index fbcfb66bd..7685b44fa 100644 --- a/src/state/stateRegistry.ts +++ b/src/state/stateRegistry.ts @@ -31,7 +31,7 @@ export class StateRegistry { listeners: StateRegistryListener[] = []; - constructor(urlMatcherFactory: UrlMatcherFactory, urlRouterProvider: UrlRouterProvider) { + constructor(urlMatcherFactory: UrlMatcherFactory, private urlRouterProvider: UrlRouterProvider) { this.matcher = new StateMatcher(this.states); this.builder = new StateBuilder(this.matcher, urlMatcherFactory); this.stateQueue = new StateQueueManager(this.states, this.builder, urlRouterProvider, this.listeners); @@ -130,7 +130,7 @@ export class StateRegistry { let deregistered = [state].concat(children).reverse(); deregistered.forEach(state => { - state.url && state.url.config.$$removeRule(); + this.urlRouterProvider.removeRule(state._urlRule); delete this.states[state.name]; }); diff --git a/src/url/urlMatcher.ts b/src/url/urlMatcher.ts index 436c67340..08ce1d710 100644 --- a/src/url/urlMatcher.ts +++ b/src/url/urlMatcher.ts @@ -112,13 +112,11 @@ export class UrlMatcher { /** * @param pattern The pattern to compile into a matcher. - * @param config A configuration object hash - * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. - * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. - * - * @property {string} pattern The pattern that was passed into the constructor + * @param config A configuration object + * - `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. + * - `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. */ - constructor(pattern: string, public config: any) { + constructor(pattern: string, public config?: any) { this.pattern = pattern; this.config = defaults(this.config, { params: {}, diff --git a/src/url/urlRouter.ts b/src/url/urlRouter.ts index d86133ce6..4d15f01d2 100644 --- a/src/url/urlRouter.ts +++ b/src/url/urlRouter.ts @@ -1,5 +1,5 @@ /** @module url */ /** for typedoc */ -import {extend, bindFunctions, IInjectable} from "../common/common"; +import {extend, bindFunctions, IInjectable, removeFrom} from "../common/common"; import {isFunction, isString, isDefined, isArray} from "../common/predicates"; import {UrlMatcher} from "./urlMatcher"; import {services, $InjectorLike, LocationServices} from "../common/coreservices"; @@ -125,13 +125,14 @@ export class UrlRouterProvider { return this; }; - /** @hidden */ - private $$removeRule(rule) { - let idx = this.rules.indexOf(rule); - if (idx !== -1) { - this.rules.splice(idx, 1); - } - return (idx !== -1); + /** + * Remove a rule previously registered + * + * @param rule the matcher rule that was previously registered using [[rule]] + * @return true if the rule was found (and removed) + */ + removeRule(rule): boolean { + return this.rules.length !== removeFrom(this.rules, rule).length; } /** @@ -202,10 +203,11 @@ export class UrlRouterProvider { * * @param what A pattern string to match, compiled as a [[UrlMatcher]]. * @param handler The path (or function that returns a path) that you want to redirect your user to. + * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]] * * Note: the handler may also invoke arbitrary code, such as `$state.go()` */ - when(what: (RegExp|UrlMatcher|string), handler: string|IInjectable) { + when(what: (RegExp|UrlMatcher|string), handler: string|IInjectable, ruleCallback = function(rule) {}) { let {$urlMatcherFactory, $stateParams} = this; let redirect, handlerIsString = isString(handler); @@ -250,9 +252,7 @@ export class UrlRouterProvider { for (var n in check) { if (check[n]) { let rule = strategies[n](what, handler); - if (check.matcher && what['config']) { - what['config'].$$removeRule = () => this.$$removeRule(rule); - } + ruleCallback(rule); return this.rule(rule); } } diff --git a/test/ng1/urlRouterSpec.js b/test/ng1/urlRouterSpec.ts similarity index 86% rename from test/ng1/urlRouterSpec.js rename to test/ng1/urlRouterSpec.ts index 6d7ffabad..ffffe6780 100644 --- a/test/ng1/urlRouterSpec.js +++ b/test/ng1/urlRouterSpec.ts @@ -1,11 +1,14 @@ +import ILocationService = angular.ILocationService; +declare var html5Compat; +import {UrlMatcher, services, UrlRouterProvider, UrlRouter, StateService} from "../../src/ng1"; +import ILocationProvider = angular.ILocationProvider; + var module = angular.mock.module; -var uiRouter = require("angular-ui-router"); -var UrlMatcher = uiRouter.UrlMatcher; -var services = uiRouter.services; describe("UrlRouter", function () { - var $urp, $lp, $s, $ur, location, match, scope; + var $urp: UrlRouterProvider, $lp: ILocationProvider, + $s: StateService, $ur: UrlRouter, location: ILocationService, match, scope; describe("provider", function () { @@ -20,7 +23,7 @@ describe("UrlRouter", function () { inject(function($rootScope, $location, $injector) { scope = $rootScope.$new(); location = $location; - $ur = $injector.invoke($urp.$get, $urp); + $ur = $injector.invoke($urp['$get'], $urp); }); }); @@ -71,9 +74,9 @@ describe("UrlRouter", function () { inject(function($rootScope, $location, $injector) { scope = $rootScope.$new(); location = $location; - $ur = $injector.invoke($urp.$get, $urp); + $ur = $injector.invoke($urp['$get'], $urp); $s = $injector.get('$sniffer'); - $s.history = true; + $s['history'] = true; }); }); @@ -155,6 +158,35 @@ describe("UrlRouter", function () { expect(called).toBeTruthy(); expect(location.path()).toBe("/b4z"); })); + + + it('removeRule should remove a previously registered rule', function() { + var count = 0, rule = function ($injector, $location) { count++; }; + $urp.rule(rule); + + $ur.sync(); + expect(count).toBe(1); + $ur.sync(); + expect(count).toBe(2); + + $urp.removeRule(rule); + $ur.sync(); + expect(count).toBe(2); + }); + + it('when should provide the new rule to the callback argument', function() { + var _rule, calls = 0; + location.url('/foo'); + $urp.when('/foo', function() { calls++; }, function(rule) { _rule = rule; }); + expect(typeof _rule).toBe('function'); + + $ur.sync(); + expect(calls).toBe(1); + + $urp.removeRule(_rule); + $ur.sync(); + expect(calls).toBe(1); + }); describe("location updates", function() { it('can push location changes', inject(function($urlRouter) { @@ -248,7 +280,7 @@ describe("UrlRouter", function () { it('should return URLs with #fragments when html5Mode is true & browser does not support pushState', inject(function($urlRouter) { $lp.html5Mode(true); - $s.history = false; + $s['history'] = false; expect(html5Compat($lp.html5Mode())).toBe(true); expect($urlRouter.href(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'})).toBe('#/hello/world#frag'); }));