Search code examples
javascriptangularangular-directiveangular13zonejs

Angular 13 window.ng.ɵcompilerFacade is replaced whenever new scripts are loaded


Update:

I noticed that whenever we try to load a component/module it looks for window.ng.ɵcompilerFacade and expects it to be in v13 complaint format. But in between, if I load any web-component(built using elements) then it is replacing the window.ng.ɵcompilerFacade which results in routing errors. I am using V13 for a shell application and inside that loading a V11 widget(built in angular elements). Not sure how to proceed further. My application will have mix and match of different versions.


Original:

I have a routerguard that loads some scripts before activating the component. This has been working fine since Angular 7 to 11. I recently upgraded to 13 and facing some weird issues.

    canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
        const result: Promise<boolean> = new Promise((resolve) => {
            if (!myElementExists) {
                this.loadApp(appObj).then(() => {
                    // success(script loaded)
                    resolve(true);
                }, () => {
                    // error - file not found / loading error
                    this.router.navigate(['/error']);
                    resolve(true);
                });
            } else {
                // success(script already loaded)
                resolve(true);
            }
        });
        return result;
}

private async loadApp(url): Promise<void> {
    await Utils.loadScript(`../${url}`);
}

This basically loads some external scripts before moving to the component. Now, with Angular 13 what happens is - I am getting the following error:

core.mjs:6494 ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'Directive')
TypeError: Cannot read properties of undefined (reading 'Directive')
    at XXXComponent.get (core.mjs:24383:1)
    at getFactoryDef (core.mjs:1406:1)
    at configureViewWithDirective (core.mjs:10484:1)
    at instantiateRootComponent (core.mjs:10191:1)
    at createRootComponent (core.mjs:12297:1)
    at ComponentFactory.create (core.mjs:21654:1)
    at ViewContainerRef.createComponent (core.mjs:22918:1)
    at RouterOutlet.activateWith (router.mjs:2521:40)
    at ActivateRoutes.activateRoutes (router.mjs:2161:40)
    at router.mjs:2111:18
    at resolvePromise (zone.js:1211:1)
    at resolvePromise (zone.js:1165:1)
    at zone.js:1278:1
    at _ZoneDelegate.invokeTask (zone.js:406:1)
    at Object.onInvokeTask (core.mjs:25595:1)
    at _ZoneDelegate.invokeTask (zone.js:405:1)
    at Zone.runTask (zone.js:178:1)
    at drainMicroTaskQueue (zone.js:585:1)
    at ZoneTask.invokeTask [as invoke] (zone.js:491:1)
    at invokeTask (zone.js:1648:1)

When I checked further, in core.mjs of V13, under addDirectiveFactoryDef() there are some changes to get compiler from facade. compiler.factoryTarget is returned as undefined.

const compiler = getCompilerFacade({ usage: 0 /* Decorator */, kind: 'directive', type });

Interesting thing is, in the canactivate() if I skip the loadscript logic and return a promise rightaway, it's working just as fine. Sometimes it happens to 'NgModule' as well, in the same getCompilerFacade. I am guessing this is something to do with the Zone.

How do I proceed from here?


Solution

  • Solution is to turn on AOT.

    In my case, both my shell app(V13) and widget app(V11) are JIT compiled. So at runtime, angular does that compiling for which it uses window.ng namespace. With 2 different versions of Angular accessing the same namespace, it broke. I now turned on aot for my shell app, it cleared it's dependency with window.ng.

    Things are working fine now.