From d86be245b8c1fdbbad603d8ddeb3b5bc6278ee74 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Sun, 31 Jan 2016 15:35:23 +0000 Subject: [PATCH] fix(angular1-router): add support for using the component helper In Angular 1.5 there is a new helper method for creating component directives. See https://docs.angularjs.org/guide/component for more information about components. These kind of directives only match the `E` element form and the previously component router only created HTML that matched directives that matched the `A` attribute form. This commit changes the `` directive so that it generates custom HTML elements rather divs with custom attributes to trigger the relevant component to appear in the DOM. Going forward, Angular 1.5 users are encouraged to create their router components using the following style: ``` myModule.componnet('component-name', { // component definition object }); ``` Closes angular/angular.js#13860 Closes #6076 Closes #5278 BREAKING CHANGE: The component router now creates custom element HTML rather than custom attribute HTML, in order to create a new component. So rather than ```html
``` it now creates ```html ``` If you defined you router components using the `directive()` helper and specified the `restrict` properties such that element matching was not allowed, e.g. `restrict: 'A'` then these components will no longer be instantiated by the component router and the outlet will be empty. The fix is to include `E` in the `restrict` property. `restrict: 'EA'` Note that this does not affect directives that did not specify the `restrict` property as the default for this property is already `EA`. --- modules/angular1_router/src/ng_outlet.ts | 3 +- .../test/integration/navigation_spec.js | 89 +++++++++++++------ npm-shrinkwrap.clean.json | 8 +- npm-shrinkwrap.json | 34 +++---- package.json | 6 +- 5 files changed, 89 insertions(+), 51 deletions(-) diff --git a/modules/angular1_router/src/ng_outlet.ts b/modules/angular1_router/src/ng_outlet.ts index 6184f7655a..7c450bb40d 100644 --- a/modules/angular1_router/src/ng_outlet.ts +++ b/modules/angular1_router/src/ng_outlet.ts @@ -155,7 +155,8 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) { } this.controller.$$routeParams = instruction.params; - this.controller.$$template = '
'; + this.controller.$$template = + '<' + dashCase(componentName) + '>'; this.controller.$$router = this.router.childRouter(instruction.componentType); let newScope = scope.$new(); diff --git a/modules/angular1_router/test/integration/navigation_spec.js b/modules/angular1_router/test/integration/navigation_spec.js index 3d230c4b12..51be632a72 100644 --- a/modules/angular1_router/test/integration/navigation_spec.js +++ b/modules/angular1_router/test/integration/navigation_spec.js @@ -21,17 +21,21 @@ describe('navigation', function () { $router = _$router_; }); - registerComponent('userCmp', { + registerDirective('userCmp', { template: '
hello {{userCmp.$routeParams.name}}
' }); - registerComponent('oneCmp', { + registerDirective('oneCmp', { template: '
{{oneCmp.number}}
', controller: function () {this.number = 'one'} }); - registerComponent('twoCmp', { + registerDirective('twoCmp', { template: '
{{twoCmp.number}}
', controller: function () {this.number = 'two'} }); + registerComponent('threeCmp', { + template: '
{{$ctrl.number}}
', + controller: function () {this.number = 'three'} + }); }); it('should work in a simple case', function () { @@ -47,6 +51,21 @@ describe('navigation', function () { expect(elt.text()).toBe('one'); }); + + it('should work with components created by the `mod.component()` helper', function () { + compile(''); + + $router.config([ + { path: '/', component: 'threeCmp' } + ]); + + $router.navigateByUrl('/'); + $rootScope.$digest(); + + expect(elt.text()).toBe('three'); + }); + + it('should navigate between components with different parameters', function () { $router.config([ { path: '/user/:name', component: 'userCmp' } @@ -68,7 +87,7 @@ describe('navigation', function () { function ParentController() { instanceCount += 1; } - registerComponent('parentCmp', { + registerDirective('parentCmp', { template: 'parent { }', $routeConfig: [ { path: '/user/:name', component: 'userCmp' } @@ -94,7 +113,7 @@ describe('navigation', function () { it('should work with nested outlets', function () { - registerComponent('childCmp', { + registerDirective('childCmp', { template: '
inner {
}
', $routeConfig: [ { path: '/b', component: 'oneCmp' } @@ -114,7 +133,7 @@ describe('navigation', function () { it('should work with recursive nested outlets', function () { - registerComponent('recurCmp', { + registerDirective('recurCmp', { template: '
recur {
}
', $routeConfig: [ { path: '/recur', component: 'recurCmp' }, @@ -163,7 +182,7 @@ describe('navigation', function () { it('should change location to the canonical route with nested components', inject(function ($location) { - registerComponent('childRouter', { + registerDirective('childRouter', { template: '
inner {
}
', $routeConfig: [ { path: '/new-child', component: 'oneCmp', name: 'NewChild'}, @@ -208,7 +227,7 @@ describe('navigation', function () { it('should expose a "navigating" property on $router', inject(function ($q) { var defer; - registerComponent('pendingActivate', { + registerDirective('pendingActivate', { $canActivate: function () { defer = $q.defer(); return defer.promise; @@ -227,36 +246,54 @@ describe('navigation', function () { expect($router.navigating).toBe(false); })); - function registerComponent(name, options) { - var controller = options.controller || function () {}; - - ['$routerOnActivate', '$routerOnDeactivate', '$routerOnReuse', '$routerCanReuse', '$routerCanDeactivate'].forEach(function (hookName) { - if (options[hookName]) { - controller.prototype[hookName] = options[hookName]; - } - }); - + function registerDirective(name, options) { function factory() { return { template: options.template || '', controllerAs: name, - controller: controller + controller: getController(options) }; } - - if (options.$canActivate) { - factory.$canActivate = options.$canActivate; - } - if (options.$routeConfig) { - factory.$routeConfig = options.$routeConfig; - } - + applyStaticProperties(factory, options); $compileProvider.directive(name, factory); } + function registerComponent(name, options) { + + var definition = { + template: options.template || '', + controller: getController(options), + } + applyStaticProperties(definition, options); + $compileProvider.component(name, definition); + } + function compile(template) { elt = $compile('
' + template + '
')($rootScope); $rootScope.$digest(); return elt; } + + function getController(options) { + var controller = options.controller || function () {}; + [ + '$routerOnActivate', '$routerOnDeactivate', + '$routerOnReuse', '$routerCanReuse', + '$routerCanDeactivate' + ].forEach(function (hookName) { + if (options[hookName]) { + controller.prototype[hookName] = options[hookName]; + } + }); + return controller; + } + + function applyStaticProperties(target, options) { + ['$canActivate', '$routeConfig'].forEach(function(property) { + if (options[property]) { + target[property] = options[property]; + } + }); + } }); + diff --git a/npm-shrinkwrap.clean.json b/npm-shrinkwrap.clean.json index 2e81c55712..fccea7ba8b 100644 --- a/npm-shrinkwrap.clean.json +++ b/npm-shrinkwrap.clean.json @@ -46,13 +46,13 @@ "version": "1.0.0" }, "angular": { - "version": "1.4.8" + "version": "1.5.0" }, "angular-animate": { - "version": "1.4.8" + "version": "1.5.0" }, "angular-mocks": { - "version": "1.4.8" + "version": "1.5.0" }, "ansi": { "version": "0.3.0" @@ -5828,5 +5828,5 @@ } }, "name": "angular-srcs", - "version": "2.0.0-beta.2" + "version": "2.0.0-beta.3" } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index dbcceace50..def5bc54f3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "dependencies": { "abbrev": { "version": "1.0.7", @@ -74,19 +74,19 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" }, "angular": { - "version": "1.4.8", - "from": "angular@>=1.4.7 <2.0.0", - "resolved": "https://registry.npmjs.org/angular/-/angular-1.4.8.tgz" + "version": "1.5.0", + "from": "angular@1.5.0", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.5.0.tgz" }, "angular-animate": { - "version": "1.4.8", - "from": "angular-animate@>=1.4.7 <2.0.0", - "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.4.8.tgz" + "version": "1.5.0", + "from": "angular-animate@1.5.0", + "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.5.0.tgz" }, "angular-mocks": { - "version": "1.4.8", - "from": "angular-mocks@>=1.4.7 <2.0.0", - "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.4.8.tgz" + "version": "1.5.0", + "from": "angular-mocks@1.5.0", + "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.5.0.tgz" }, "ansi": { "version": "0.3.0", @@ -7668,7 +7668,7 @@ }, "rxjs": { "version": "5.0.0-beta.0", - "from": "rxjs@5.0.0-beta.0", + "from": "https://registry.npmjs.org/rxjs/-/rxjs-5.0.0-beta.0.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.0.0-beta.0.tgz" }, "sass-graph": { @@ -8519,29 +8519,29 @@ }, "ts2dart": { "version": "0.7.22", - "from": "ts2dart@>=0.7.22 <0.8.0", + "from": "https://registry.npmjs.org/ts2dart/-/ts2dart-0.7.22.tgz", "resolved": "https://registry.npmjs.org/ts2dart/-/ts2dart-0.7.22.tgz", "dependencies": { "source-map": { "version": "0.4.4", - "from": "source-map@>=0.4.2 <0.5.0", + "from": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz" }, "source-map-support": { "version": "0.3.3", - "from": "source-map-support@>=0.3.1 <0.4.0", + "from": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.3.3.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.3.3.tgz", "dependencies": { "source-map": { "version": "0.1.32", - "from": "source-map@0.1.32", + "from": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz" } } }, "typescript": { "version": "1.7.3", - "from": "typescript@1.7.3", + "from": "https://registry.npmjs.org/typescript/-/typescript-1.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.7.3.tgz" } } @@ -9292,7 +9292,7 @@ }, "zone.js": { "version": "0.5.13", - "from": "zone.js@0.5.13", + "from": "https://registry.npmjs.org/zone.js/-/zone.js-0.5.13.tgz", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.5.13.tgz" } } diff --git a/package.json b/package.json index 93c8e91f18..e659aa9136 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,9 @@ "zone.js": "0.5.13" }, "devDependencies": { - "angular": "^1.4.7", - "angular-animate": "^1.4.7", - "angular-mocks": "^1.4.7", + "angular": "^1.5.0", + "angular-animate": "^1.5.0", + "angular-mocks": "^1.5.0", "base64-js": "^0.0.8", "bower": "^1.3.12", "broccoli": "^0.16.9",