Problem

Let’s say you have a well-developed app written in Vue. It’s pretty big and has multiple teams working in the code base for both maintenance and feature development. Now, let’s say you need to include that app in an Angular app and you want to avoid transposing the Vue app to AngularThis recently occurred on one of our projects. The task was to incorporate a Vue.js component into an existing Angular app. Several hang-ups eventually lead to rewriting the Angular piece in Vue. The following walkthrough is to create a proof of concept to demonstrate that adding a Vue component to an Angular app is possible

Setup

I’ll be using a premade Vue Calculator to add to an Angular Tour of Heroes app. These should serve well enough as standins for the real-world problem. I’ll be using VSCode throughout, however, any text editor ought to do. 

Vue

The first step is to take inventory of the apps we’ll be using. To do that I’ll pull the calculator repo down locally and install all dependencies.  

Vue Formula

Now that the app is local and dependencies installed, I’ll check it out by serving the Vue app. 

Vue Formula 2

Then navigate to http://localhost:8080 in the browser.

Vue Calculator Snapshot

This step strictly speaking isn’t necessary, however, should something go wrong later on it’s good to know we at least started with functional components.

Now you can build this Vue app as a web component. To do this, I’ll be modifying the npm scripts located in package.json. Open package.json in VSCode and copy the build script. Paste it directly below the copied build script and change the name, I’ll be naming mine build-wc to signify that it builds a web component. Next, I’ll prepend some flags to the build-wc script.

Build WC Formula

The target flag tells the cli how to build the Vue app; I’ll use wc since we want to create a web component. Additional build target information can be found on the Vue CLI docs build targets page. The name flag will determine the output JavaScript file name in addition to the HTML tag I will use in angular where I want the calculator to appear. Finally, the last parameter is the component I want to use as the entry point. Since the Vue Calculator consists of only one component, you could use either App.vue or Calculator.vue as the final argument and have similar results. By using uCalculator.vue it illustrates how you can use a direct component instead of an entire app. Now that I have created the web component build script, I’ll run it and again inspect the results.

Refun the WC

I can now see a /dist directory with several files.

dist directory formula

I’ll install and use the npm package to serve this up and view the web component in the browser.

Install and use npm package

Now I can open the browser and navigate to http://127.0.0.1:8080/demo.html.

Vue Calc Demo

Opening the developer tools in Chrome and viewing the Elements tab reveals the custom <my-vue-calc></my-vue-calc> mentioned earlier; I’ll include the web component the same way when I get to the integration. For now, you can once again see the calculator working as normal. You may notice that the calculator looks slightly different from the first time the app was served. This is due to the app having been built as a web component with Calculator.vue as the entry point. This demonstrates how styling works within Vue apps. In App.vue, the calculator I am using has a style block with several styles added to the #app selector. However, since I built only the Calculator component of the larger app, I don’t get the styles from App.vue. For demo purposes, I’ll ignore the text alignment and font colors of the number keys. There are several gotchas when dealing with web-components so check out the Vue CLI docs for additional info.

Angular

Now that I am satisfied with the results of building the Vue web component I’ll pull down the Angular app to which I’ll be adding the calculator.

Angular App snapshot

And just like before, I’ll install dependencies.

Installing dependencies

I was having conflicts with the node-sass package and the version of node I had installed, v12.9.1. Using , I changed the current version of node to v11.15.0 which solved that problem. Once the app is cloned and dependencies are installed, I served the app, again, to ensure that it’s working prior to any changes I make. To accomplish this I ran the following in the root directory of the Tour of Heroes app.
ng serve snapshot

Integration

Now that the app is working I added the custom web component html tag to index.html.

Adding HTML web component

I’ve added the ‘TEST’ text as a sanity check. Since the web component isn’t defined yet, the tag defaults to the functionality of a <div>. Looking at the browser again there is now immediately following the <my-root></my-root>.
Test HTML Check in Vue

So far so good.

For organization purposes, I added a my-vue-calc directory nested in libs to hold the actual my-vue-calc.min.js code that I copied from the dist directory of the vue-calc app.
Add vue-calc app directory

In order to use the my-vue-calc component I need to load Vue.js in the Angular app. I accomplished this by installing vue as a dependency using npm.
Laod Vue to Angular App

Then adding vue to the angular.json configuration file. While I’m here I’ll also add the calculator to the scripts.
Angular Jason Configuration

Having modified angular.json you will likely have to stop and restart the server for the changes to be picked up and compiled. With that, the angular app should rebuild itself and refresh the webpage and the calculator should appear at the bottom.

Angular App Restart

Well… sort of… this is a very simplistic example and implemented in the simplest way. What if I want the web component in one of the angular components? Doing this would break up the source code a bit more and perhaps allow for lazy loading of the components. I like the idea of having a calculator on the individual hero detail pages. To achieve this, I’ll remove the custom element tag from index.html and move it to the bottom of app/hero-details.component.html. Navigating to a hero details page, right away a console error appears.

Navigating a Hero details page

The custom element should render a <div> with ‘TEST’ since I removed the web component JavaScript from the angular.json config file. To correct the error, I added the following to the @NgModule decorator.

Custom Elements formula snapshot

The CUSTOM_ELEMENTS_SCHEMA tells angular that you will be using elements that are neither Angular components nor directives in the app, check out the Angular Docs for additional info. The ‘TEST’ text now appears on the hero details page.
Test Text in hero details

The only thing left to do is import the web component JavaScript. In this case, I’ll just add an import statement to the hero-detail.component.ts.
Import Web component JavaScript

And like magic once more!

Test Text

In summary, the example in this walkthrough is absolutely contrived. I could have just as easily brought a native angular calculator app into the Tour of Heroes. However, there is no need for any calculator Vue or otherwise where I’ve added it. This walkthrough is to demonstrate the capability of adding a large well maintained Vue app to an Angular app while avoiding rewrite of the Vue app and the additional maintenance of a new code base. There may be caveats that make this solution unworkable for your application, so use your best judgment. The Angular and Vue docs are an excellent resource for additional information on these topics and many more check them out here and here.

Works Cited

Angular Docs. (2019, September 19). Retrieved from Angular.io: https://angular.io/docs

Butler, K. (2019, September 24). Vue Calculator. Retrieved from GitHub: https://github.com/kylbutlr/vue-calculator

Papa, J. (2019, September 24). Angular Tour of Heroes. Retrieved from GitHub: https://github.com/johnpapa/angular-tour-of-heroes

Vue CLI Docs. (2019, September 19). Retrieved from Vue CLI: https://cli.vuejs.org/guide/