From 42b4b6d21b5c629bf938a19afc4196cfb0ea4813 Mon Sep 17 00:00:00 2001 From: Christoph Krautz Date: Thu, 29 Sep 2016 18:45:28 +0200 Subject: [PATCH] fix(upgrade): bind optional properties when upgrading from ng1 (#11411) Previously, optional properties of a directive/component would be wrongly mapped and thus ignored. Closes #10181 --- .../upgrade/src/upgrade_ng1_adapter.ts | 5 +- modules/@angular/upgrade/test/upgrade_spec.ts | 75 +++++++++++++++---- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts b/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts index cc168f2d80..7d1c41b595 100644 --- a/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts +++ b/modules/@angular/upgrade/src/upgrade_ng1_adapter.ts @@ -86,7 +86,10 @@ export class UpgradeNg1ComponentAdapterBuilder { if ((context).hasOwnProperty(name)) { var localName = context[name]; var type = localName.charAt(0); - localName = localName.substr(1) || name; + var typeOptions = localName.charAt(1); + localName = typeOptions === '?' ? localName.substr(2) : localName.substr(1); + localName = localName || name; + var outputName = 'output_' + name; var outputNameRename = outputName + ': ' + name; var outputNameRenameChange = outputName + ': ' + name + 'Change'; diff --git a/modules/@angular/upgrade/test/upgrade_spec.ts b/modules/@angular/upgrade/test/upgrade_spec.ts index f32afed002..52bbbd2731 100644 --- a/modules/@angular/upgrade/test/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/upgrade_spec.ts @@ -297,12 +297,12 @@ export function main() { describe('upgrade ng1 component', () => { it('should bind properties, events', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); - var ng1Module = angular.module('ng1', []); + const ng1Module = angular.module('ng1', []); - var ng1 = function() { + const ng1 = () => { return { - template: 'Hello {{fullName}}; A: {{dataA}}; B: {{dataB}}; | ', - scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', event: '&'}, + template: 'Hello {{fullName}}; A: {{dataA}}; B: {{dataB}}; C: {{modelC}}; | ', + scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', modelC: '=', event: '&'}, link: function(scope: any /** TODO #9100 */) { scope.$watch('dataB', (v: any /** TODO #9100 */) => { if (v == 'Savkin') { @@ -317,37 +317,82 @@ export function main() { }; }; ng1Module.directive('ng1', ng1); - var Ng2 = + const Ng2 = Component({ selector: 'ng2', template: - '' + - '' + - '{{event}}-{{last}}, {{first}}' + '' + + '{{event}}-{{last}}, {{first}}, {{city}}' }).Class({ constructor: function() { this.first = 'Victor'; this.last = 'Savkin'; + this.city = 'SF'; this.event = '?'; } }); - var Ng2Module = NgModule({ - declarations: [adapter.upgradeNg1Component('ng1'), Ng2], - imports: [BrowserModule], - schemas: [NO_ERRORS_SCHEMA], - }).Class({constructor: function() {}}); + const Ng2Module = NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }).Class({constructor: function() {}}); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); - var element = html(`
`); + const element = html(`
`); adapter.bootstrap(element, ['ng1']).ready((ref) => { // we need to do setTimeout, because the EventEmitter uses setTimeout to schedule // events, and so without this we would not see the events processed. setTimeout(() => { expect(multiTrim(document.body.textContent)) .toEqual( - 'Hello SAVKIN, Victor; A: VICTOR; B: SAVKIN; | Hello TEST; A: First; B: Last; | WORKS-SAVKIN, Victor'); + 'Hello SAVKIN, Victor, SF; A: VICTOR; B: SAVKIN; C: SF; | Hello TEST; A: First; B: Last; C: City; | WORKS-SAVKIN, Victor, SF'); + ref.dispose(); + }, 0); + }); + })); + + it('should bind optional properties', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + + const ng1 = () => { + return { + template: 'Hello; A: {{dataA}}; B: {{modelB}}; | ', + scope: {modelA: '=?dataA', modelB: '=?'} + }; + }; + ng1Module.directive('ng1', ng1); + const Ng2 = Component({ + selector: 'ng2', + template: '' + + '' + + '' + + '' + }).Class({ + constructor: function() { + this.first = 'Victor'; + this.last = 'Savkin'; + } + }); + + const Ng2Module = NgModule({ + declarations: [adapter.upgradeNg1Component('ng1'), Ng2], + imports: [BrowserModule], + schemas: [NO_ERRORS_SCHEMA], + }).Class({constructor: function() {}}); + + ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); + const element = html(`
`); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + // we need to do setTimeout, because the EventEmitter uses setTimeout to schedule + // events, and so without this we would not see the events processed. + setTimeout(() => { + expect(multiTrim(document.body.textContent)) + .toEqual( + 'Hello; A: Victor; B: Savkin; | Hello; A: First; B: Last; | Hello; A: ; B: ; | Hello; A: ; B: ; |'); ref.dispose(); }, 0); });