Running Aurelia along with (inside) angular 1.x app

Is it possible and safe to run Aurelia app inside an angular app?
Are there any issues I should be aware of before I decide to go that way? Any possible unexpected behaviors or conflicts?
I have a large legacy app to maintain, and would love to start using Aurelia for new features (new views/screens). Unfortunately the entire app layout and routing is built with angular and it’s too complex to be rebuilt at this moment.
I wonder if anyone has tried embedding small Aurelia-based pieces inside an angular app?
Any feedback is very appreciated!

Aurelia inside Angular should be good as long as you do not mix Routers. Using Aurelias progressive enhancement allows to use Aurelia wherever you need it to. As for page changes, depending in your Router, use the appropriate event and enhance parts of the Dom with Aurelia.

We also have a legacy app in Angular 1.x but we decided not to mix and match. The eventual goal is to port everything to Aurelia but in the meantime all new features served up completely separately.

server 1: angular app
server 2: aurelia app

We’re lucky though that the feature set we’re building is almost completely independent between the two. The downside is the UI is slightly different and “single sign-on” is painful. The strategy we’re using for SSO is pushing a JWT token via an iframe from server1 to server2. After that JWT makes everything easy.

I’ll answer my own question for anyone planning to do anything similar.

I’ve decided to build an application consisting of independent components, each of them is currently running inside of an Angular app. Once we have ported every view, we’ll simply set-up the routing and nav menu and with some minor changes (like handling router view lifecycle along the component lifecycle) we should be able to get rid completely of Angular app.

In order to be able to run Aurelia inside of Angular views, I’ve defined a new webpack entrypoint
angularIntegration: [’./src/angular-integration.ts’]
and added all the modules in given subdirectory there (this is important, as there is no Router definition, so webpack-aurelia won’t discover and add the modules by itself)

new GlobDependenciesPlugin({
  'angular-integration': ['src/main-modules/**/*']
}),

as the Aurelia index.html is built manually, I don’t want it to be generated from index.ejs template, I’ve also added AssetsPlugin (more on this later)

new AssetsPlugin({filename: 'dist/assets.json', fullPath: false}),

now, this is my aureliaIntegration startup code:

function configure(aurelia: Aurelia, moduleName: string, host: Element) {
 // ... standard aurelia configuration
  aurelia.start().then(() => {
    aurelia.setRoot(moduleName, host);
  });
}

console.debug("angular<>aurelia integration entry point");
(window as any).startAurelia = (moduleName: string) => {
  bootstrapper.bootstrap((aurelia) => configure(aurelia, moduleName, document.querySelector('div[aurelia-app]')));
}

I know setting startAurelia method on window is not very elegant, but it was the fastest way to share the aurelia bootstrapper with simple, plain, no-loader old JS code.

now on Angular side, I’ve added the assets to index.html (as the index.html is static and not generated, I’ve created a simple ASP.NET core middleware which handles the routes like “js/aurelia-integration.js” and serves the content of appropriate JS file, looking up its name in assets.json)

for every view defined in Angular that I want to replace with specific Aurelia module, I use this simple template code

<div aurelia-app>	
</div>

<script>
window.startAurelia('main-modules/password-reset/password-remind');
</script>

also, to be able to access route (state) info in Aurelia, on Angular side I’ve configured to store it in window

$rootScope.$on('$stateChangeSuccess', function(ev, toState, toParams, fromState, fromParams) {
   if (!window._angular_aurelia_integration )
   	window._angular_aurelia_integration = {};
   window._angular_aurelia_integration.state = {
   	name: toState,
   	params: toParams
   };
});

which is then used in Aurelia app

  interface IAngularState {
    name: string;
    params?: any;
}

export function currentState(): IAngularState {
    // see app.run angular & $stateChangeSuccess
    let integration = (window as any)._angular_aurelia_integration;
    if (integration)
        return integration.state;
    return undefined;
}

I know this could be much more elegant and performant, but we’re aiming for getting away from Angular as soon as possible :slight_smile:

1 Like