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
This commit is contained in:
Christoph Krautz 2016-09-29 18:45:28 +02:00 committed by Chuck Jazdzewski
parent 36bc2ff269
commit 42b4b6d21b
2 changed files with 64 additions and 16 deletions

View File

@ -86,7 +86,10 @@ export class UpgradeNg1ComponentAdapterBuilder {
if ((<any>context).hasOwnProperty(name)) { if ((<any>context).hasOwnProperty(name)) {
var localName = context[name]; var localName = context[name];
var type = localName.charAt(0); 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 outputName = 'output_' + name;
var outputNameRename = outputName + ': ' + name; var outputNameRename = outputName + ': ' + name;
var outputNameRenameChange = outputName + ': ' + name + 'Change'; var outputNameRenameChange = outputName + ': ' + name + 'Change';

View File

@ -297,12 +297,12 @@ export function main() {
describe('upgrade ng1 component', () => { describe('upgrade ng1 component', () => {
it('should bind properties, events', async(() => { it('should bind properties, events', async(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var ng1Module = angular.module('ng1', []); const ng1Module = angular.module('ng1', []);
var ng1 = function() { const ng1 = () => {
return { return {
template: 'Hello {{fullName}}; A: {{dataA}}; B: {{dataB}}; | ', template: 'Hello {{fullName}}; A: {{dataA}}; B: {{dataB}}; C: {{modelC}}; | ',
scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', event: '&'}, scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', modelC: '=', event: '&'},
link: function(scope: any /** TODO #9100 */) { link: function(scope: any /** TODO #9100 */) {
scope.$watch('dataB', (v: any /** TODO #9100 */) => { scope.$watch('dataB', (v: any /** TODO #9100 */) => {
if (v == 'Savkin') { if (v == 'Savkin') {
@ -317,37 +317,82 @@ export function main() {
}; };
}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = const Ng2 =
Component({ Component({
selector: 'ng2', selector: 'ng2',
template: template:
'<ng1 fullName="{{last}}, {{first}}" [modelA]="first" [(modelB)]="last" ' + '<ng1 fullName="{{last}}, {{first}}, {{city}}" [modelA]="first" [(modelB)]="last" [modelC]="city" ' +
'(event)="event=$event"></ng1>' + '(event)="event=$event"></ng1>' +
'<ng1 fullName="{{\'TEST\'}}" modelA="First" modelB="Last"></ng1>' + '<ng1 fullName="{{\'TEST\'}}" modelA="First" modelB="Last" modelC="City"></ng1>' +
'{{event}}-{{last}}, {{first}}' '{{event}}-{{last}}, {{first}}, {{city}}'
}).Class({ }).Class({
constructor: function() { constructor: function() {
this.first = 'Victor'; this.first = 'Victor';
this.last = 'Savkin'; this.last = 'Savkin';
this.city = 'SF';
this.event = '?'; this.event = '?';
} }
}); });
var Ng2Module = NgModule({ const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2], declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule], imports: [BrowserModule],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
}).Class({constructor: function() {}}); }).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); const element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule // we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
// events, and so without this we would not see the events processed. // events, and so without this we would not see the events processed.
setTimeout(() => { setTimeout(() => {
expect(multiTrim(document.body.textContent)) expect(multiTrim(document.body.textContent))
.toEqual( .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: '<ng1 [modelA]="first" [modelB]="last"></ng1>' +
'<ng1 modelA="First" modelB="Last"></ng1>' +
'<ng1></ng1>' +
'<ng1></ng1>'
}).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(`<div><ng2></ng2></div>`);
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(); ref.dispose();
}, 0); }, 0);
}); });