diff --git a/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts b/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts index c6549e8923..fa536a47e2 100644 --- a/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts +++ b/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts @@ -78,17 +78,15 @@ export class DowngradeComponentAdapter { let expr: any /** TODO #9100 */ = null; if (attrs.hasOwnProperty(input.attr)) { - const observeFn = ((prop: any /** TODO #9100 */) => { + const observeFn = (prop => { let prevValue = INITIAL_VALUE; - return (value: any /** TODO #9100 */) => { - if (this.inputChanges !== null) { - this.inputChangeCount++; - this.inputChanges[prop] = new SimpleChange( - value, prevValue === INITIAL_VALUE ? value : prevValue, - prevValue === INITIAL_VALUE); - prevValue = value; + return (currValue: any) => { + if (prevValue === INITIAL_VALUE) { + prevValue = currValue; } - this.component[prop] = value; + + this.updateInput(prop, prevValue, currValue); + prevValue = currValue; }; })(input.prop); attrs.$observe(input.attr, observeFn); @@ -104,14 +102,8 @@ export class DowngradeComponentAdapter { } if (expr != null) { const watchFn = - ((prop: any /** TODO #9100 */) => ( - value: any /** TODO #9100 */, prevValue: any /** TODO #9100 */) => { - if (this.inputChanges != null) { - this.inputChangeCount++; - this.inputChanges[prop] = new SimpleChange(prevValue, value, prevValue === value); - } - this.component[prop] = value; - })(input.prop); + (prop => (currValue: any, prevValue: any) => + this.updateInput(prop, prevValue, currValue))(input.prop); this.componentScope.$watch(expr, watchFn); } } @@ -185,4 +177,13 @@ export class DowngradeComponentAdapter { } getInjector(): Injector { return this.componentRef && this.componentRef.injector; } + + private updateInput(prop: string, prevValue: any, currValue: any) { + if (this.inputChanges) { + this.inputChangeCount++; + this.inputChanges[prop] = new SimpleChange(prevValue, currValue, prevValue === currValue); + } + + this.component[prop] = currValue; + } } diff --git a/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts b/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts index 1db48add84..db05c40801 100644 --- a/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts @@ -290,9 +290,11 @@ export function main() { it('should bind properties, events', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - const ng1Module = angular.module('ng1', []); + const ng1Module = + angular.module('ng1', []).value('$exceptionHandler', (err: any) => { throw err; }); ng1Module.run(($rootScope: any) => { + $rootScope.name = 'world'; $rootScope.dataA = 'A'; $rootScope.dataB = 'B'; $rootScope.modelA = 'initModelA'; @@ -363,9 +365,10 @@ export function main() { break; case 1: assertChange('twoWayA', 'newA'); + assertChange('twoWayB', 'newB'); break; case 2: - assertChange('twoWayB', 'newB'); + assertChange('interpolate', 'Hello everyone'); break; default: throw new Error('Called too many times! ' + JSON.stringify(changes)); @@ -380,7 +383,7 @@ export function main() { }).Class({constructor: function() {}}); const element = html(`
- @@ -393,6 +396,15 @@ export function main() { 'literal: Text; interpolate: Hello world; ' + 'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' + 'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); + + ref.ng1RootScope.$apply('name = "everyone"'); + expect(multiTrim(document.body.textContent)) + .toEqual( + 'ignore: -; ' + + 'literal: Text; interpolate: Hello everyone; ' + + 'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' + + 'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); + ref.dispose(); }); diff --git a/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts b/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts index 25783719e2..59339416dc 100644 --- a/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts +++ b/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts @@ -13,7 +13,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import * as angular from '@angular/upgrade/src/common/angular1'; import {UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; -import {bootstrap, html, multiTrim} from '../test_helpers'; +import {$apply, bootstrap, html, multiTrim} from '../test_helpers'; export function main() { describe('downgrade ng2 component', () => { @@ -22,15 +22,18 @@ export function main() { afterEach(() => destroyPlatform()); it('should bind properties, events', async(() => { - - const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => { - $rootScope['dataA'] = 'A'; - $rootScope['dataB'] = 'B'; - $rootScope['modelA'] = 'initModelA'; - $rootScope['modelB'] = 'initModelB'; - $rootScope['eventA'] = '?'; - $rootScope['eventB'] = '?'; - }); + const ng1Module = + angular.module('ng1', []).value('$exceptionHandler', (err: any) => { + throw err; + }).run(($rootScope: angular.IScope) => { + $rootScope['name'] = 'world'; + $rootScope['dataA'] = 'A'; + $rootScope['dataB'] = 'B'; + $rootScope['modelA'] = 'initModelA'; + $rootScope['modelB'] = 'initModelB'; + $rootScope['eventA'] = '?'; + $rootScope['eventB'] = '?'; + }); @Component({ selector: 'ng2', @@ -94,9 +97,10 @@ export function main() { break; case 1: assertChange('twoWayA', 'newA'); + assertChange('twoWayB', 'newB'); break; case 2: - assertChange('twoWayB', 'newB'); + assertChange('interpolate', 'Hello everyone'); break; default: throw new Error('Called too many times! ' + JSON.stringify(changes)); @@ -125,7 +129,7 @@ export function main() { const element = html(`
- @@ -139,6 +143,14 @@ export function main() { 'literal: Text; interpolate: Hello world; ' + 'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' + 'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); + + $apply(upgrade, 'name = "everyone"'); + expect(multiTrim(document.body.textContent)) + .toEqual( + 'ignore: -; ' + + 'literal: Text; interpolate: Hello everyone; ' + + 'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' + + 'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); }); })); diff --git a/modules/@angular/upgrade/test/static/integration/upgrade_component_spec.ts b/modules/@angular/upgrade/test/static/integration/upgrade_component_spec.ts index 812f55f8a8..855e9ede00 100644 --- a/modules/@angular/upgrade/test/static/integration/upgrade_component_spec.ts +++ b/modules/@angular/upgrade/test/static/integration/upgrade_component_spec.ts @@ -13,7 +13,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import * as angular from '@angular/upgrade/src/common/angular1'; import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static'; -import {bootstrap, digest, html, multiTrim} from '../test_helpers'; +import {$digest, bootstrap, html, multiTrim} from '../test_helpers'; export function main() { describe('upgrade ng1 component', () => { @@ -530,7 +530,7 @@ export function main() { ng2ComponentInstance.dataA = 'foo2'; ng2ComponentInstance.dataB = 'bar2'; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(element.textContent)) @@ -605,7 +605,7 @@ export function main() { ng2ComponentInstance.dataA = {value: 'foo2'}; ng2ComponentInstance.dataB = {value: 'bar2'}; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(element.textContent)) @@ -682,7 +682,7 @@ export function main() { ng2ComponentInstance.dataA = {value: 'foo2'}; ng2ComponentInstance.dataB = {value: 'bar2'}; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(element.textContent)) @@ -929,7 +929,7 @@ export function main() { ng1Controller1.outputA({value: 'foo again'}); ng1Controller2.outputB('bar again'); - digest(adapter); + $digest(adapter); tick(); expect(ng1Controller0.inputA).toEqual({value: 'foo again'}); @@ -1010,7 +1010,7 @@ export function main() { .toBe('ng1 - Data: [4,5] - Length: 2 | ng2 - Data: 4,5 - Length: 2'); ng1Controller.$scope.outputA(6); - digest(adapter); + $digest(adapter); tick(); expect(ng1Controller.$scope.inputA).toEqual([4, 5, 6]); @@ -1858,7 +1858,7 @@ export function main() { // Change: Re-assign `data` ng2ComponentInstance.data = {foo: 'baz'}; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChanges.calls.count()).toBe(2); @@ -1879,7 +1879,7 @@ export function main() { // No change: Update internal property ng2ComponentInstance.data.foo = 'qux'; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChanges.calls.count()).toBe(2); @@ -1888,7 +1888,7 @@ export function main() { // Change: Re-assign `data` (even if it looks the same) ng2ComponentInstance.data = {foo: 'qux'}; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChanges.calls.count()).toBe(3); @@ -2008,7 +2008,7 @@ export function main() { // Change: Re-assign `data` ng2ComponentInstance.data = {foo: 'baz'}; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChangesA.calls.count()).toBe(2); @@ -2029,7 +2029,7 @@ export function main() { // No change: Update internal property ng2ComponentInstance.data.foo = 'qux'; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChangesA.calls.count()).toBe(2); @@ -2039,7 +2039,7 @@ export function main() { // Change: Re-assign `data` (even if it looks the same) ng2ComponentInstance.data = {foo: 'qux'}; - digest(adapter); + $digest(adapter); tick(); expect(scopeOnChangesA.calls.count()).toBe(3); @@ -2399,12 +2399,12 @@ export function main() { // Run a `$digest` // (Since it's the first one since the `$doCheck` watcher was added, // the `watchFn` will be run twice.) - digest(adapter); + $digest(adapter); expect(controllerDoCheckA.calls.count()).toBe(3); expect(controllerDoCheckB.calls.count()).toBe(3); // Run another `$digest` - digest(adapter); + $digest(adapter); expect(controllerDoCheckA.calls.count()).toBe(4); expect(controllerDoCheckB.calls.count()).toBe(4); }); @@ -2474,11 +2474,11 @@ export function main() { expect(scopeDoCheck).not.toHaveBeenCalled(); // Run a `$digest` - digest(adapter); + $digest(adapter); expect(scopeDoCheck).not.toHaveBeenCalled(); // Run another `$digest` - digest(adapter); + $digest(adapter); expect(scopeDoCheck).not.toHaveBeenCalled(); }); })); @@ -2788,7 +2788,7 @@ export function main() { expect(scopeDestroyListener).not.toHaveBeenCalled(); ng2ComponentAInstance.destroyIt = true; - digest(adapter); + $digest(adapter); expect(scopeDestroyListener).toHaveBeenCalled(); }); @@ -2952,7 +2952,7 @@ export function main() { // (Should not propagate upwards.) ng2ComponentBInstance.ng2BInputA = 'foo2'; ng2ComponentBInstance.ng2BInputC = 'baz2'; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -2962,7 +2962,7 @@ export function main() { // (Should propagate all the way up to `ng1ADataC` and back all the way down to // `ng2BInputC`.) ng2ComponentBInstance.ng2BOutputC.emit('baz3'); - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -2972,7 +2972,7 @@ export function main() { // (Should not propagate upwards, only downwards.) ng1ControllerXInstance.ng1XInputA = 'foo4'; ng1ControllerXInstance.ng1XInputB = {value: 'bar4'}; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -2981,7 +2981,7 @@ export function main() { // Update `ng1XInputC`. // (Should propagate upwards and downwards.) ng1ControllerXInstance.ng1XInputC = {value: 'baz5'}; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -2990,7 +2990,7 @@ export function main() { // Update a property on `ng1XInputC`. // (Should propagate upwards and downwards.) ng1ControllerXInstance.ng1XInputC.value = 'baz6'; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -2999,7 +2999,7 @@ export function main() { // Emit from `ng1XOutputA`. // (Should propagate upwards to `ng1ADataA` and back all the way down to `ng2BInputA`.) (ng1ControllerXInstance as any).ng1XOutputA({value: 'foo7'}); - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -3009,7 +3009,7 @@ export function main() { // (Should propagate upwards to `ng1ADataB`, but not downwards, // since `ng1XInputB` has been re-assigned (i.e. `ng2ADataB !== ng1XInputB`).) (ng1ControllerXInstance as any).ng1XOutputB('bar8'); - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) @@ -3020,7 +3020,7 @@ export function main() { ng2ComponentAInstance.ng2ADataA = {value: 'foo9'}; ng2ComponentAInstance.ng2ADataB = {value: 'bar9'}; ng2ComponentAInstance.ng2ADataC = {value: 'baz9'}; - digest(adapter); + $digest(adapter); tick(); expect(multiTrim(document.body.textContent)) diff --git a/modules/@angular/upgrade/test/static/test_helpers.ts b/modules/@angular/upgrade/test/static/test_helpers.ts index 14edabe1d9..903d3732fb 100644 --- a/modules/@angular/upgrade/test/static/test_helpers.ts +++ b/modules/@angular/upgrade/test/static/test_helpers.ts @@ -24,7 +24,12 @@ export function bootstrap( }); } -export function digest(adapter: UpgradeModule) { +export function $apply(adapter: UpgradeModule, exp: angular.Ng1Expression) { + const $rootScope = adapter.$injector.get($ROOT_SCOPE) as angular.IRootScopeService; + $rootScope.$apply(exp); +} + +export function $digest(adapter: UpgradeModule) { const $rootScope = adapter.$injector.get($ROOT_SCOPE) as angular.IRootScopeService; $rootScope.$digest(); }