From 31f48ae94318c5a7b8fad10158221fbcb756fc6b Mon Sep 17 00:00:00 2001 From: Shahar Talmi Date: Mon, 28 Sep 2015 02:12:23 +0300 Subject: [PATCH] refactor(ngOutlet): using some typescript features Closes #4386 --- modules/angular1_router/src/ng_outlet.ts | 339 +++++++++++------------ modules/angular1_router/tsd.json | 12 + package.json | 2 +- 3 files changed, 177 insertions(+), 176 deletions(-) create mode 100644 modules/angular1_router/tsd.json diff --git a/modules/angular1_router/src/ng_outlet.ts b/modules/angular1_router/src/ng_outlet.ts index 535205043c..a01ff92de1 100644 --- a/modules/angular1_router/src/ng_outlet.ts +++ b/modules/angular1_router/src/ng_outlet.ts @@ -1,69 +1,51 @@ -'use strict'; - -/* - * A module for adding new a routing system Angular 1. - */ -angular.module('ngComponentRouter', []) - .directive('ngOutlet', ngOutletDirective) - .directive('ngOutlet', ngOutletFillContentDirective) - .directive('ngLink', ngLinkDirective); - -/* - * A module for inspecting controller constructors - */ -angular.module('ng') - .provider('$$directiveIntrospector', $$directiveIntrospectorProvider) - .config(compilerProviderDecorator); +/// /* * decorates $compileProvider so that we have access to routing metadata */ -function compilerProviderDecorator($compileProvider, $$directiveIntrospectorProvider) { - var directive = $compileProvider.directive; - $compileProvider.directive = function (name, factory) { +function compilerProviderDecorator($compileProvider, + $$directiveIntrospectorProvider: DirectiveIntrospectorProvider) { + let directive = $compileProvider.directive; + $compileProvider.directive = function(name: string, factory: Function) { $$directiveIntrospectorProvider.register(name, factory); return directive.apply(this, arguments); }; } - /* * private service that holds route mappings for each controller */ -function $$directiveIntrospectorProvider() { - var directiveBuffer = []; - var directiveFactoriesByName = {}; - var onDirectiveRegistered = null; - return { - register: function (name, factory) { - if (angular.isArray(factory)) { - factory = factory[factory.length - 1]; - } - directiveFactoriesByName[name] = factory; - if (onDirectiveRegistered) { - onDirectiveRegistered(name, factory); - } else { - directiveBuffer.push({name: name, factory: factory}); - } - }, - $get: function () { - var fn = function (newOnControllerRegistered) { - onDirectiveRegistered = newOnControllerRegistered; - while (directiveBuffer.length > 0) { - var directive = directiveBuffer.pop(); - onDirectiveRegistered(directive.name, directive.factory); - } - }; +class DirectiveIntrospectorProvider { + private directiveBuffer: any[] = []; + private directiveFactoriesByName: {[name: string]: Function} = {}; + private onDirectiveRegistered: (name: string, factory: Function) => any = null; - fn.getTypeByName = function (name) { - return directiveFactoriesByName[name]; - }; - - return fn; + register(name: string, factory: Function) { + if (angular.isArray(factory)) { + factory = factory[factory.length - 1]; } - }; -} + this.directiveFactoriesByName[name] = factory; + if (this.onDirectiveRegistered) { + this.onDirectiveRegistered(name, factory); + } else { + this.directiveBuffer.push({name: name, factory: factory}); + } + } + $get() { + let fn: any = newOnControllerRegistered => { + this.onDirectiveRegistered = newOnControllerRegistered; + while (this.directiveBuffer.length > 0) { + let directive = this.directiveBuffer.pop(); + this.onDirectiveRegistered(directive.name, directive.factory); + } + }; + + fn.getTypeByName = name => this.directiveFactoriesByName[name]; + + return fn; + } +} /** * @name ngOutlet @@ -79,8 +61,8 @@ function $$directiveIntrospectorProvider() { * * The value for the `ngOutlet` attribute is optional. */ -function ngOutletDirective($animate, $q, $router) { - var rootRouter = $router; +function ngOutletDirective($animate, $q: ng.IQService, $router) { + let rootRouter = $router; return { restrict: 'AE', @@ -89,117 +71,120 @@ function ngOutletDirective($animate, $q, $router) { priority: 400, require: ['?^^ngOutlet', 'ngOutlet'], link: outletLink, - controller: function () {}, + controller: class {}, controllerAs: '$$ngOutlet' }; - function outletLink(scope, $element, attrs, ctrls, $transclude) { - var outletName = attrs.ngOutlet || 'default', - parentCtrl = ctrls[0], - myCtrl = ctrls[1], - router = (parentCtrl && parentCtrl.$$router) || rootRouter; + function outletLink(scope, element, attrs, ctrls, $transclude) { + class Outlet { + constructor(private controller, private router) {} - myCtrl.$$currentComponent = null; + private currentController; + private currentInstruction; + private currentScope; + private currentElement; + private previousLeaveAnimation; - var childRouter, - currentController, - currentInstruction, - currentScope, - currentElement, - previousLeaveAnimation; + private cleanupLastView() { + if (this.previousLeaveAnimation) { + $animate.cancel(this.previousLeaveAnimation); + this.previousLeaveAnimation = null; + } - function cleanupLastView() { - if (previousLeaveAnimation) { - $animate.cancel(previousLeaveAnimation); - previousLeaveAnimation = null; + if (this.currentScope) { + this.currentScope.$destroy(); + this.currentScope = null; + } + if (this.currentElement) { + this.previousLeaveAnimation = $animate.leave(this.currentElement); + this.previousLeaveAnimation.then(() => this.previousLeaveAnimation = null); + this.currentElement = null; + } } - if (currentScope) { - currentScope.$destroy(); - currentScope = null; - } - if (currentElement) { - previousLeaveAnimation = $animate.leave(currentElement); - previousLeaveAnimation.then(function () { - previousLeaveAnimation = null; - }); - currentElement = null; - } - } - - router.registerPrimaryOutlet({ - reuse: function (instruction) { - var next = $q.when(true); - var previousInstruction = currentInstruction; - currentInstruction = instruction; - if (currentController && currentController.$onReuse) { - next = $q.when(currentController.$onReuse(currentInstruction, previousInstruction)); + reuse(instruction) { + let next = $q.when(true); + let previousInstruction = this.currentInstruction; + this.currentInstruction = instruction; + if (this.currentController && this.currentController.$onReuse) { + next = $q.when( + this.currentController.$onReuse(this.currentInstruction, previousInstruction)); } return next; - }, - canReuse: function (nextInstruction) { - var result; - if (!currentInstruction || - currentInstruction.componentType !== nextInstruction.componentType) { + } + + canReuse(nextInstruction) { + let result; + if (!this.currentInstruction || + this.currentInstruction.componentType !== nextInstruction.componentType) { result = false; - } else if (currentController && currentController.$canReuse) { - result = currentController.$canReuse(nextInstruction, currentInstruction); + } else if (this.currentController && this.currentController.$canReuse) { + result = this.currentController.$canReuse(nextInstruction, this.currentInstruction); } else { - result = nextInstruction === currentInstruction || - angular.equals(nextInstruction.params, currentInstruction.params); + result = nextInstruction === this.currentInstruction || + angular.equals(nextInstruction.params, this.currentInstruction.params); } return $q.when(result); - }, - canDeactivate: function (instruction) { - if (currentController && currentController.$canDeactivate) { - return $q.when(currentController.$canDeactivate(instruction, currentInstruction)); + } + + canDeactivate(instruction) { + if (this.currentController && this.currentController.$canDeactivate) { + return $q.when( + this.currentController.$canDeactivate(instruction, this.currentInstruction)); } return $q.when(true); - }, - deactivate: function (instruction) { - if (currentController && currentController.$onDeactivate) { - return $q.when(currentController.$onDeactivate(instruction, currentInstruction)); - } - return $q.when(); - }, - activate: function (instruction) { - var previousInstruction = currentInstruction; - currentInstruction = instruction; + } - var componentName = myCtrl.$$componentName = instruction.componentType; - - if (typeof componentName != 'string') { - throw new Error('Component is not a string for ' + instruction.urlPath); - } - - myCtrl.$$routeParams = instruction.params; - - myCtrl.$$template = '
'; - - myCtrl.$$router = router.childRouter(instruction.componentType); - - var newScope = scope.$new(); - - var clone = $transclude(newScope, function (clone) { - $animate.enter(clone, null, currentElement || $element); - cleanupLastView(); - }); - - - currentElement = clone; - currentScope = newScope; - - // TODO: prefer the other directive retrieving the controller - // by debug mode - currentController = currentElement.children().eq(0).controller(componentName); - - if (currentController && currentController.$onActivate) { - return currentController.$onActivate(instruction, previousInstruction); + deactivate(instruction) { + if (this.currentController && this.currentController.$onDeactivate) { + return $q.when( + this.currentController.$onDeactivate(instruction, this.currentInstruction)); } return $q.when(); } - }); + + activate(instruction) { + let previousInstruction = this.currentInstruction; + this.currentInstruction = instruction; + + let componentName = this.controller.$$componentName = instruction.componentType; + + if (typeof componentName !== 'string') { + throw new Error('Component is not a string for ' + instruction.urlPath); + } + + this.controller.$$routeParams = instruction.params; + this.controller.$$template = '
'; + this.controller.$$router = this.router.childRouter(instruction.componentType); + + let newScope = scope.$new(); + + let clone = $transclude(newScope, clone => { + $animate.enter(clone, null, this.currentElement || element); + this.cleanupLastView(); + }); + + this.currentElement = clone; + this.currentScope = newScope; + + // TODO: prefer the other directive retrieving the controller + // by debug mode + this.currentController = this.currentElement.children().eq(0).controller(componentName); + + if (this.currentController && this.currentController.$onActivate) { + return this.currentController.$onActivate(instruction, previousInstruction); + } + return $q.when(); + } + } + + let parentCtrl = ctrls[0], myCtrl = ctrls[1], + router = (parentCtrl && parentCtrl.$$router) || rootRouter; + + myCtrl.$$currentComponent = null; + + router.registerPrimaryOutlet(new Outlet(myCtrl, router)); } } /** @@ -210,14 +195,14 @@ function ngOutletFillContentDirective($compile) { restrict: 'EA', priority: -400, require: 'ngOutlet', - link: function (scope, $element, attrs, ctrl) { - var template = ctrl.$$template; - $element.html(template); - var link = $compile($element.contents()); + link: (scope, element, attrs, ctrl) => { + let template = ctrl.$$template; + element.html(template); + let link = $compile(element.contents()); link(scope); // TODO: move to primary directive - var componentInstance = scope[ctrl.$$componentName]; + let componentInstance = scope[ctrl.$$componentName]; if (componentInstance) { ctrl.$$currentComponent = componentInstance; @@ -228,7 +213,6 @@ function ngOutletFillContentDirective($compile) { }; } - /** * @name ngLink * @description @@ -240,9 +224,9 @@ function ngOutletFillContentDirective($compile) { * ## Example * * ```js - * angular.module('myApp', ['ngFuturisticRouter']) + * angular.module('myApp', ['ngComponentRouter']) * .controller('AppController', ['$router', function($router) { - * $router.config({ path: '/user/:id' component: 'user' }); + * $router.config({ path: '/user/:id', component: 'user' }); * this.user = { name: 'Brian', id: 123 }; * }); * ``` @@ -254,42 +238,35 @@ function ngOutletFillContentDirective($compile) { * ``` */ function ngLinkDirective($router, $parse) { - var rootRouter = $router; + let rootRouter = $router; - return { - require: '?^^ngOutlet', - restrict: 'A', - link: ngLinkDirectiveLinkFn - }; + return {require: '?^^ngOutlet', restrict: 'A', link: ngLinkDirectiveLinkFn}; - function ngLinkDirectiveLinkFn(scope, elt, attrs, ctrl) { - var router = (ctrl && ctrl.$$router) || rootRouter; + function ngLinkDirectiveLinkFn(scope, element, attrs, ctrl) { + let router = (ctrl && ctrl.$$router) || rootRouter; if (!router) { return; } - var instruction = null; - var link = attrs.ngLink || ''; + let instruction = null; + let link = attrs.ngLink || ''; function getLink(params) { instruction = router.generate(params); return './' + angular.stringifyInstruction(instruction); } - var routeParamsGetter = $parse(link); + let routeParamsGetter = $parse(link); // we can avoid adding a watcher if it's a literal if (routeParamsGetter.constant) { - var params = routeParamsGetter(); - elt.attr('href', getLink(params)); + let params = routeParamsGetter(); + element.attr('href', getLink(params)); } else { - scope.$watch(function () { - return routeParamsGetter(scope); - }, function (params) { - elt.attr('href', getLink(params)); - }, true); + scope.$watch(() => routeParamsGetter(scope), params => element.attr('href', getLink(params)), + true); } - elt.on('click', function (event) { + element.on('click', event => { if (event.which !== 1 || !instruction) { return; } @@ -300,9 +277,21 @@ function ngLinkDirective($router, $parse) { } } - -function dashCase(str) { - return str.replace(/([A-Z])/g, function ($1) { - return '-' + $1.toLowerCase(); - }); +function dashCase(str: string): string { + return str.replace(/[A-Z]/g, match => '-' + match.toLowerCase()); } + +/* + * A module for adding new a routing system Angular 1. + */ +angular.module('ngComponentRouter', []) + .directive('ngOutlet', ngOutletDirective) + .directive('ngOutlet', ngOutletFillContentDirective) + .directive('ngLink', ngLinkDirective); + +/* + * A module for inspecting controller constructors + */ +angular.module('ng') + .provider('$$directiveIntrospector', DirectiveIntrospectorProvider) + .config(compilerProviderDecorator); diff --git a/modules/angular1_router/tsd.json b/modules/angular1_router/tsd.json new file mode 100644 index 0000000000..481137d5eb --- /dev/null +++ b/modules/angular1_router/tsd.json @@ -0,0 +1,12 @@ +{ + "version": "v4", + "repo": "angular/DefinitelyTyped", + "ref": "master", + "path": "typings", + "bundle": "typings/tsd.d.ts", + "installed": { + "angularjs/angular.d.ts": { + "commit": "746b9a892629060bc853e792afff536e0ec4655e" + } + } +} diff --git a/package.json b/package.json index b02a7ea96b..7da5418b58 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "scripts": { "preinstall": "node tools/npm/check-node-modules --purge", - "postinstall": "node tools/npm/copy-npm-shrinkwrap && node tools/chromedriverpatch.js && webdriver-manager update && bower install && gulp pubget.dart && tsd reinstall --overwrite --clean --config modules/angular2/tsd.json && tsd reinstall --overwrite --clean --config tools/tsd.json", + "postinstall": "node tools/npm/copy-npm-shrinkwrap && node tools/chromedriverpatch.js && webdriver-manager update && bower install && gulp pubget.dart && tsd reinstall --overwrite --clean --config modules/angular2/tsd.json && tsd reinstall --overwrite --clean --config tools/tsd.json && tsd reinstall --overwrite --config modules/angular1_router/tsd.json", "test": "gulp test.all.js && gulp test.all.dart" }, "dependencies": {