From 247964af62ab89f917153b417b9afe4bc59e6f98 Mon Sep 17 00:00:00 2001 From: Kevin Merckx Date: Mon, 14 Mar 2016 07:51:04 +0100 Subject: [PATCH] fix(upgrade): make upgradeAdapter upgrade angular 1 components correctly With this fix, the $onInit function of an upgraded angular 1 component is called and input bindings (<) are created. Closes #7951 --- modules/angular2/src/upgrade/angular_js.ts | 10 ++++ .../src/upgrade/upgrade_ng1_adapter.ts | 10 ++++ modules/angular2/test/upgrade/upgrade_spec.ts | 59 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/modules/angular2/src/upgrade/angular_js.ts b/modules/angular2/src/upgrade/angular_js.ts index bdd75dbb66..1c1fdf452f 100644 --- a/modules/angular2/src/upgrade/angular_js.ts +++ b/modules/angular2/src/upgrade/angular_js.ts @@ -1,6 +1,7 @@ export interface IModule { config(fn: any): IModule; directive(selector: string, factory: any): IModule; + component(selector: string, component: IComponent): IModule; controller(name: string, type: any): IModule; factory(key: string, factoryFn: any): IModule; value(key: string, value: any): IModule; @@ -59,6 +60,15 @@ export interface IDirectiveLinkFn { (scope: IScope, instanceElement: IAugmentedJQuery, instanceAttributes: IAttributes, controller: any, transclude: ITranscludeFunction): void; } +export interface IComponent { + bindings?: Object; + controller?: any; + controllerAs?: string; + require?: any; + template?: any; + templateUrl?: any; + transclude?: any; +} export interface IAttributes { $observe(attr: string, fn: (v: string) => void): void; } export interface ITranscludeFunction { // If the scope is provided, then the cloneAttachFn must be as well. diff --git a/modules/angular2/src/upgrade/upgrade_ng1_adapter.ts b/modules/angular2/src/upgrade/upgrade_ng1_adapter.ts index 2b1a1da136..ad69eaea5a 100644 --- a/modules/angular2/src/upgrade/upgrade_ng1_adapter.ts +++ b/modules/angular2/src/upgrade/upgrade_ng1_adapter.ts @@ -53,6 +53,7 @@ export class UpgradeNg1ComponentAdapterBuilder { self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap); } ], + ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ }, ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ }, ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ } }); @@ -106,6 +107,8 @@ export class UpgradeNg1ComponentAdapterBuilder { this.propertyMap[outputName] = localName; // don't break; let it fall through to '@' case '@': + // handle the '<' binding of angular 1.5 components + case '<': this.inputs.push(inputName); this.inputsRename.push(inputNameRename); this.propertyMap[inputName] = localName; @@ -231,6 +234,13 @@ class UpgradeNg1ComponentAdapter implements OnChanges, DoCheck { } } + + ngOnInit() { + if (this.destinationObj.$onInit) { + this.destinationObj.$onInit(); + } + } + ngOnChanges(changes: {[name: string]: SimpleChange}) { for (var name in changes) { if ((changes).hasOwnProperty(name)) { diff --git a/modules/angular2/test/upgrade/upgrade_spec.ts b/modules/angular2/test/upgrade/upgrade_spec.ts index d60483d6c5..638b6226ae 100644 --- a/modules/angular2/test/upgrade/upgrade_spec.ts +++ b/modules/angular2/test/upgrade/upgrade_spec.ts @@ -561,6 +561,65 @@ export function main() { }); })); + it('should call $onInit of components', inject([AsyncTestCompleter], (async) => { + var adapter = new UpgradeAdapter(); + var ng1Module = angular.module('ng1', []); + var valueToFind = '$onInit'; + + var ng1 = { + bindings: {}, + template: '{{$ctrl.value}}', + controller: Class( + {constructor: function() {}, $onInit: function() { this.value = valueToFind; }}) + }; + ng1Module.component('ng1', ng1); + + var Ng2 = Component({ + selector: 'ng2', + template: '', + directives: [adapter.upgradeNg1Component('ng1')] + }).Class({constructor: function() {}}); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + + var element = html(`
`); + adapter.bootstrap(element, ['ng1']) + .ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual(valueToFind); + ref.dispose(); + async.done(); + }); + })); + + it('should bind input properties (<) of components', inject([AsyncTestCompleter], (async) => { + var adapter = new UpgradeAdapter(); + var ng1Module = angular.module('ng1', []); + + var ng1 = { + bindings: {personProfile: '<'}, + template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}', + controller: Class({constructor: function() {}}) + }; + ng1Module.component('ng1', ng1); + + var Ng2 = + Component({ + selector: 'ng2', + template: '', + directives: [adapter.upgradeNg1Component('ng1')] + }) + .Class({ + constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; } + }); + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + + var element = html(`
`); + adapter.bootstrap(element, ['ng1']) + .ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`); + ref.dispose(); + async.done(); + }); + })); }); describe('injection', () => {