Skip to content

Commit 0193be7

Browse files
gkalpakJason Aden
authored andcommitted
feat(upgrade): fix support for directive.link in upgraded components
(#17971) Although, pre- and post-linking functions are correctly called during directive linking, directives with `link.post` would throw an error. Interestingly, having `link.pre` only or defining `link: fn` (which is an alias for `link.post: fn`) would not throw. This commit removes this check and allows directives with pre- and/or post-linking functions to work.
1 parent 2ea7351 commit 0193be7

File tree

2 files changed

+238
-6
lines changed

2 files changed

+238
-6
lines changed

packages/upgrade/src/common/upgrade_helper.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,6 @@ export class UpgradeHelper {
9090
if (directive.terminal) this.notSupported('terminal');
9191
if (directive.compile) this.notSupported('compile');
9292

93-
const link = directive.link;
94-
// QUESTION: why not support link.post?
95-
if (typeof link == 'object') {
96-
if (link.post) this.notSupported('link.post');
97-
}
98-
9993
return directive;
10094
}
10195

packages/upgrade/test/static/integration/upgrade_component_spec.ts

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,244 @@ export function main() {
10691069
}));
10701070
});
10711071

1072+
describe('linking', () => {
1073+
it('should run the pre-linking after instantiating the controller', async(() => {
1074+
const log: string[] = [];
1075+
1076+
// Define `ng1Directive`
1077+
const ng1Directive: angular.IDirective = {
1078+
template: '',
1079+
link: {pre: () => log.push('ng1-pre')},
1080+
controller: class {constructor() { log.push('ng1-ctrl'); }}
1081+
};
1082+
1083+
// Define `Ng1ComponentFacade`
1084+
@Directive({selector: 'ng1'})
1085+
class Ng1ComponentFacade extends UpgradeComponent {
1086+
constructor(elementRef: ElementRef, injector: Injector) {
1087+
super('ng1', elementRef, injector);
1088+
}
1089+
}
1090+
1091+
// Define `Ng2Component`
1092+
@Component({selector: 'ng2', template: '<ng1></ng1>'})
1093+
class Ng2Component {
1094+
}
1095+
1096+
// Define `ng1Module`
1097+
const ng1Module = angular.module('ng1', [])
1098+
.directive('ng1', () => ng1Directive)
1099+
.directive('ng2', downgradeComponent({component: Ng2Component}));
1100+
1101+
// Define `Ng2Module`
1102+
@NgModule({
1103+
imports: [BrowserModule, UpgradeModule],
1104+
declarations: [Ng1ComponentFacade, Ng2Component],
1105+
entryComponents: [Ng2Component],
1106+
})
1107+
class Ng2Module {
1108+
ngDoBootstrap() {}
1109+
}
1110+
1111+
// Bootstrap
1112+
const element = html(`<ng2></ng2>`);
1113+
1114+
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
1115+
expect(log).toEqual(['ng1-ctrl', 'ng1-pre']);
1116+
});
1117+
}));
1118+
1119+
it('should run the pre-linking function before linking', async(() => {
1120+
const log: string[] = [];
1121+
1122+
// Define `ng1Directive`
1123+
const ng1DirectiveA: angular.IDirective = {
1124+
template: '<ng1-b></ng1-b>',
1125+
link: {pre: () => log.push('ng1A-pre')}
1126+
};
1127+
1128+
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
1129+
1130+
// Define `Ng1ComponentAFacade`
1131+
@Directive({selector: 'ng1A'})
1132+
class Ng1ComponentAFacade extends UpgradeComponent {
1133+
constructor(elementRef: ElementRef, injector: Injector) {
1134+
super('ng1A', elementRef, injector);
1135+
}
1136+
}
1137+
1138+
// Define `Ng2Component`
1139+
@Component({selector: 'ng2', template: '<ng1A></ng1A>'})
1140+
class Ng2Component {
1141+
}
1142+
1143+
// Define `ng1Module`
1144+
const ng1Module = angular.module('ng1', [])
1145+
.directive('ng1A', () => ng1DirectiveA)
1146+
.directive('ng1B', () => ng1DirectiveB)
1147+
.directive('ng2', downgradeComponent({component: Ng2Component}));
1148+
1149+
// Define `Ng2Module`
1150+
@NgModule({
1151+
imports: [BrowserModule, UpgradeModule],
1152+
declarations: [Ng1ComponentAFacade, Ng2Component],
1153+
entryComponents: [Ng2Component],
1154+
})
1155+
class Ng2Module {
1156+
ngDoBootstrap() {}
1157+
}
1158+
1159+
// Bootstrap
1160+
const element = html(`<ng2></ng2>`);
1161+
1162+
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
1163+
expect(log).toEqual(['ng1A-pre', 'ng1B-post']);
1164+
});
1165+
}));
1166+
1167+
it('should run the post-linking function after linking (link: object)', async(() => {
1168+
const log: string[] = [];
1169+
1170+
// Define `ng1Directive`
1171+
const ng1DirectiveA: angular.IDirective = {
1172+
template: '<ng1-b></ng1-b>',
1173+
link: {post: () => log.push('ng1A-post')}
1174+
};
1175+
1176+
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
1177+
1178+
// Define `Ng1ComponentAFacade`
1179+
@Directive({selector: 'ng1A'})
1180+
class Ng1ComponentAFacade extends UpgradeComponent {
1181+
constructor(elementRef: ElementRef, injector: Injector) {
1182+
super('ng1A', elementRef, injector);
1183+
}
1184+
}
1185+
1186+
// Define `Ng2Component`
1187+
@Component({selector: 'ng2', template: '<ng1A></ng1A>'})
1188+
class Ng2Component {
1189+
}
1190+
1191+
// Define `ng1Module`
1192+
const ng1Module = angular.module('ng1', [])
1193+
.directive('ng1A', () => ng1DirectiveA)
1194+
.directive('ng1B', () => ng1DirectiveB)
1195+
.directive('ng2', downgradeComponent({component: Ng2Component}));
1196+
1197+
// Define `Ng2Module`
1198+
@NgModule({
1199+
imports: [BrowserModule, UpgradeModule],
1200+
declarations: [Ng1ComponentAFacade, Ng2Component],
1201+
entryComponents: [Ng2Component],
1202+
})
1203+
class Ng2Module {
1204+
ngDoBootstrap() {}
1205+
}
1206+
1207+
// Bootstrap
1208+
const element = html(`<ng2></ng2>`);
1209+
1210+
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
1211+
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
1212+
});
1213+
}));
1214+
1215+
it('should run the post-linking function after linking (link: function)', async(() => {
1216+
const log: string[] = [];
1217+
1218+
// Define `ng1Directive`
1219+
const ng1DirectiveA: angular.IDirective = {
1220+
template: '<ng1-b></ng1-b>',
1221+
link: () => log.push('ng1A-post')
1222+
};
1223+
1224+
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
1225+
1226+
// Define `Ng1ComponentAFacade`
1227+
@Directive({selector: 'ng1A'})
1228+
class Ng1ComponentAFacade extends UpgradeComponent {
1229+
constructor(elementRef: ElementRef, injector: Injector) {
1230+
super('ng1A', elementRef, injector);
1231+
}
1232+
}
1233+
1234+
// Define `Ng2Component`
1235+
@Component({selector: 'ng2', template: '<ng1A></ng1A>'})
1236+
class Ng2Component {
1237+
}
1238+
1239+
// Define `ng1Module`
1240+
const ng1Module = angular.module('ng1', [])
1241+
.directive('ng1A', () => ng1DirectiveA)
1242+
.directive('ng1B', () => ng1DirectiveB)
1243+
.directive('ng2', downgradeComponent({component: Ng2Component}));
1244+
1245+
// Define `Ng2Module`
1246+
@NgModule({
1247+
imports: [BrowserModule, UpgradeModule],
1248+
declarations: [Ng1ComponentAFacade, Ng2Component],
1249+
entryComponents: [Ng2Component],
1250+
})
1251+
class Ng2Module {
1252+
ngDoBootstrap() {}
1253+
}
1254+
1255+
// Bootstrap
1256+
const element = html(`<ng2></ng2>`);
1257+
1258+
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
1259+
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
1260+
});
1261+
}));
1262+
1263+
it('should run the post-linking function before `$postLink`', async(() => {
1264+
const log: string[] = [];
1265+
1266+
// Define `ng1Directive`
1267+
const ng1Directive: angular.IDirective = {
1268+
template: '',
1269+
link: () => log.push('ng1-post'),
1270+
controller: class {$postLink() { log.push('ng1-$post'); }}
1271+
};
1272+
1273+
// Define `Ng1ComponentFacade`
1274+
@Directive({selector: 'ng1'})
1275+
class Ng1ComponentFacade extends UpgradeComponent {
1276+
constructor(elementRef: ElementRef, injector: Injector) {
1277+
super('ng1', elementRef, injector);
1278+
}
1279+
}
1280+
1281+
// Define `Ng2Component`
1282+
@Component({selector: 'ng2', template: '<ng1></ng1>'})
1283+
class Ng2Component {
1284+
}
1285+
1286+
// Define `ng1Module`
1287+
const ng1Module = angular.module('ng1', [])
1288+
.directive('ng1', () => ng1Directive)
1289+
.directive('ng2', downgradeComponent({component: Ng2Component}));
1290+
1291+
// Define `Ng2Module`
1292+
@NgModule({
1293+
imports: [BrowserModule, UpgradeModule],
1294+
declarations: [Ng1ComponentFacade, Ng2Component],
1295+
entryComponents: [Ng2Component],
1296+
})
1297+
class Ng2Module {
1298+
ngDoBootstrap() {}
1299+
}
1300+
1301+
// Bootstrap
1302+
const element = html(`<ng2></ng2>`);
1303+
1304+
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
1305+
expect(log).toEqual(['ng1-post', 'ng1-$post']);
1306+
});
1307+
}));
1308+
});
1309+
10721310
describe('controller', () => {
10731311
it('should support `controllerAs`', async(() => {
10741312
// Define `ng1Directive`

0 commit comments

Comments
 (0)