What is the alternative to the deprecated SystemJsNgModuleLoader function in Angular 12?

Viewed 273

With the arrival of Angular 13 the SystemJsNgModuleLoader function is deprecated. I have just upgraded my Angular 12 and I needed this function in order to translate the routes (for SEO) in a multi-language site. But with Angular 12, my service doesn't seem to work anymore. I would like to know what is the substitute function to SystemJsNgModuleLoader?

this is the code :

localize.loader.ts

import { ROUTES, Route, Routes } from '@angular/router';
import {
  SystemJsNgModuleLoader, NgModuleFactory, Injector,
  SystemJsNgModuleLoaderConfig, Optional, Compiler, Injectable, Inject
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import * as utils from '../classes/localize.utils';
import { SITE_INFO, SfSiteInfo } from 'sf-lib';

/**
 * Extension of SystemJsNgModuleLoader to enable localization of route on lazy load
 */
@Injectable()
export class SfLocalizeLoader extends SystemJsNgModuleLoader {

  constructor(
    private translate: TranslateService,
    @Inject(SITE_INFO) private siteInfo: SfSiteInfo,
    _compiler: Compiler,
    @Optional() config?: SystemJsNgModuleLoaderConfig
  ) {
    super(_compiler, config);
  }

  /**
   * Extend load with custom functionality
   */
  load(path: string): Promise<NgModuleFactory<any>> {
    return super.load(path).then((factory: NgModuleFactory<any>) => {
      return {
        moduleType: factory.moduleType,
        create: (parentInjector: Injector) => {
          const module = factory.create(parentInjector);
          const getMethod = module.injector.get.bind(module.injector);
          module.injector['get'] = (token: any, notFoundValue: any) => {
            const getResult = getMethod(token, notFoundValue);

            if (token === ROUTES) {
              const newRoutes: Routes = [].concat(...getResult);
              newRoutes.forEach(
                (route: Route) => this.confRoute(route)
              );
              return newRoutes;
            } else {
              return getResult;
            }
          };
          return module;
        }
      };
    });
  }

  private confRoute(route: Route) {
    if (!route.data) {
      route.data = {};
    }
    if (route.path && route.path !== '' && route.path !== '**') {
      route.data['path'] = utils.anonymize(route.path, this.siteInfo.defaultLang, this.siteInfo.translationObject);
      route.path = utils.unanonymize(route.data.path, this.siteInfo.lang, this.siteInfo.translationObject);
    }
    if (route.redirectTo && route.redirectTo !== '' && route.redirectTo !== '**') {
      route.data['redirectTo'] = utils.anonymize(route.redirectTo, this.siteInfo.defaultLang, this.siteInfo.translationObject);
      route.redirectTo = utils.unanonymize(route.data.redirectTo, this.siteInfo.lang, this.siteInfo.translationObject);
    }
    if (route.children) {
      route.children.forEach(
        (child: Route) => {
          this.confRoute(child);
        }
      );
    }
  }
}

localize.module.ts

import { NgModule, ModuleWithProviders, NgModuleFactoryLoader } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SfLocalizeService } from './services/localize.service';
import { SfLocalizeLoader } from './services/localize.loader';

import { SfLocalizePipe } from './pipes/localize.pipe';
import { SfLang, SfSiteInfo, SITE_INFO } from 'sf-lib';
import { SfLocalizeConfig } from './models/localize-config.model';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [SfLocalizePipe],
  exports: [SfLocalizePipe]
})
export class SfLocalizeModule {
  static forRoot(config: SfLocalizeConfig): ModuleWithProviders<SfLocalizeModule> {
    return {
      ngModule: SfLocalizeModule,
      providers: [
        config.siteInfo ? config.siteInfo :
        {
          provide: SITE_INFO,
          useFactory: dummySiteInfo
        },
        SfLocalizeService,
        { provide: NgModuleFactoryLoader, useClass: SfLocalizeLoader }
      ],
    };
  }
}

export function dummySiteInfo(): SfSiteInfo {
  const lang: SfLang = {
    sfType: 'SfLang',
    country: 'com',
    code: 'en',
    default: true
  };
  const si: SfSiteInfo = {
    lang: lang,
    locales: [lang],
    defaultLang: lang,
    path: '',
    translationObject: {},
    origin: ''
  };
  return si;
}

app.module.ts

import { NgModule, Injector, APP_INITIALIZER, PLATFORM_ID, ErrorHandler, Injectable } from '@angular/core';
import { isPlatformServer, Location } from '@angular/common';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { extractExtension, extractLang } from './shared/utils/translate/translate.utils'
 
import {
  SfLib,
  SfSiteInfo,
  SITE_INFO,
} from 'sf-lib';
import { site } from '@assets/mock/site.mock';

import { AppRoutingModule, routes } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
import { AppInjector } from './core/app-injector';

import { environment } from '@env/environment';
import { translationObject } from '@assets/translationObject';
import { CookieModule } from 'ngx-cookie';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { BrowserModule, HammerGestureConfig, HAMMER_GESTURE_CONFIG, TransferState } from '@angular/platform-browser';
import { GlobalErrorHandlerService } from './core/error-handler/services/error-handler.service';
import { SfLocalizeService } from './core/localize/services/localize.service';
import { SfLocalizeModule } from './core/localize/localize.module';
import { LocalizeParser, LocalizeRouterModule, LocalizeRouterSettings } from '@gilsdav/ngx-translate-router';
import { localizeBrowserLoaderFactory } from './core/localize/services/localize-browser.loader';

/** @fixme HACK ! https://github.com/angular/material2/issues/5488 */
@NgModule({
  providers: [
    {
      provide: APP_INITIALIZER,
      deps: [MatIconRegistry],
      useFactory: preserveSvgViewBox,
      multi: true
    }
  ]
})
export class IconModule {
}

// override hammerjs default configuration
@Injectable()
export class MyHammerConfig extends HammerGestureConfig {
  overrides = <any>{
      // tslint:disable-next-line
      'swipe': { direction: 2 | 4},
      'pinch': { enable: false },
      'rotate': { enable: false },
      'pan': { enable: false }
  };
}

export function preserveSvgViewBox(matIconRegistry: MatIconRegistry) {
  return () => {
    const oldToSvgElement = matIconRegistry['_toSvgElement'];

    matIconRegistry['_toSvgElement'] = (element: Element) => {
      const svg = oldToSvgElement.call(matIconRegistry, element);

      const viewBox = element.getAttribute('viewBox');
      if (viewBox !== null) {
        svg.setAttribute('viewBox', viewBox);
      }
      return svg;
    };
  };
}

export function httpTranslateLoader(http: HttpClient) {
  return new TranslateHttpLoader(http);
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule.withServerTransition({appId: 'sombrero'}),
    CookieModule.forRoot(),

    AppRoutingModule,

    CoreModule,

    MatIconModule,
    IconModule,
    HttpClientModule,

    // SFLIB
    SfLib.forRoot({
      apiUrl: environment.url
    }),
    SfLocalizeModule.forRoot({
      siteInfo: {
        provide: SITE_INFO,
        useFactory: localizeInit,
        deps: [Injector, PLATFORM_ID]
      }
    }),
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: httpTranslateLoader,
        deps: [HttpClient]
      }
    }),
    LocalizeRouterModule.forRoot(routes, {
      parser: {
        provide: LocalizeParser,
        useFactory: localizeBrowserLoaderFactory,
        deps: [TranslateService, Location, LocalizeRouterSettings, HttpClient, TransferState],
      },
      initialNavigation: true,
    })
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: localizeServiceInit,
      deps: [Injector],
      multi: true
    },
    {
      provide: HAMMER_GESTURE_CONFIG,
      useClass: MyHammerConfig
    },
    // Error handling
    {
      provide: ErrorHandler,
      useClass: GlobalErrorHandlerService
    }
  ]
})

export class AppModule {
  constructor(
    injector: Injector
  ) {
    // Store module's injector in the AppInjector class
    AppInjector.setInjector(injector);
  }
}

export function localizeServiceInit(injector: Injector) {
  return () => {
    injector.get(SfLocalizeService);
  };
}


export function localizeInit(injector: Injector, platformId: string) {
  const siteInfo: SfSiteInfo = new SfSiteInfo();
  let hostExtension: string;
  let lang: string;
  if (isPlatformServer(platformId)) {
    // Fetch HOSTNAME value token defined in server.ts (when run on server)
    hostExtension = extractExtension(injector.get('HOSTNAME'));
    siteInfo.lang.country = site.find((s) => s.lang.country === hostExtension) ? hostExtension : 'com';

    siteInfo.locales = site
    .filter((s) => s.lang.country === siteInfo.lang.country)
    .map((s) => s.lang);

    siteInfo.path = injector.get('PATH');
    (siteInfo as any).origin = injector.get('ORIGIN').replace(':443', '');

    lang = extractLang(siteInfo.path, siteInfo.locales);
    if (!lang) {
      const foundSiteLang: {country: string, code: string} = environment.siteLang.find((sl) => sl.country === siteInfo.lang.country);
      lang = foundSiteLang ? foundSiteLang.code : siteInfo.lang.country;
      siteInfo.lang.default = true;
    } else if (lang && lang === siteInfo.lang.country) {
      siteInfo.lang.default = true;
    } else {
      siteInfo.lang.default = false;
    }
    siteInfo.lang.code = lang;
  } else {
    hostExtension = extractExtension((<any>window).location.hostname);
    siteInfo.lang.country = site.find((s) => s.lang.country === hostExtension) ? hostExtension : 'com';

    siteInfo.locales = site
    .filter((s) => s.lang.country === siteInfo.lang.country)
    .map((s) => s.lang);

    siteInfo.path = (<any>window).location.pathname;
    (siteInfo as any).origin = (<any>window).location.origin;

    lang = extractLang(siteInfo.path, siteInfo.locales);

    if (!lang) {
      const foundSiteLang: {country: string, code: string} = environment.siteLang.find((sl) => sl.country === siteInfo.lang.country);
      lang = foundSiteLang ? foundSiteLang.code : siteInfo.lang.country;
      siteInfo.lang.default = true;
    } else if (lang && lang === siteInfo.lang.country) {
      siteInfo.lang.default = true;
    } else {
      siteInfo.lang.default = false;
    }
    siteInfo.lang.code = lang;

  }
  siteInfo.defaultLang = {
    sfType: 'SfLang',
    country: 'com',
    code: 'en',
    default: true
  };
  const translate = injector.get(TranslateService);
  translate.setDefaultLang(`${siteInfo.lang.code}`);
  console.log(translate.currentLang)

  return siteInfo;
}

Thanks for your help!

0 Answers
Related