{"id":27478,"date":"2019-02-20T17:27:59","date_gmt":"2019-02-20T16:27:59","guid":{"rendered":"http:\/\/matteo.piazza"},"modified":"2024-01-16T12:49:49","modified_gmt":"2024-01-16T11:49:49","slug":"migrating-angular-to-vue-2","status":"publish","type":"post","link":"https:\/\/exmachina.ch\/en\/tech\/migrating-angular-to-vue-2\/","title":{"rendered":"Migrating an Angular 1.x app to Vue 2.x"},"content":{"rendered":"\n<h3>A ridiculously detailed and opinionated attempt to let Angular and Vue peacefully live together (if you wanna rock\u2019n\u2019roll)<\/h3>\n<p>Sometimes you have to say &#8220;stop!&#8221; and decide it&#8217;s time to migrate to a warmer and sunnier place.<\/p>\n\n\n\n<p>Chances are that you are quietly writing code to grow up and fix your shiny-happy-die-hard-godzilla app in Angular 1.x, day by day (with a certain amount of satisfaction, why not?).<br \/>In the meantime, as Darwin would say (the man, not the OS), javascript species evolve over time. And, not so surprisingly, you wake up one day to discover that you and your creature are slowly fading to black (&#8220;I cannot stand this hell I feel&#8230;&#8221; you know).<br \/>Reasons can vary: Angular 1.x will no longer be supported soon; you can indeed write a better javascript today; your application can improve in performance and maintainability&#8230; you name it.<br \/>So no choices here, actually, winter&#8217;s coming: time to migrate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Disclaimer<\/h3>\n\n\n\n<p>I will not share here the reasons why we choose Vue over alternative frameworks: that&#8217;s not the scope of this writing. I do not want to suggest that Angular is the nuclear winter, and Vue a fresh breeze in a mild Tuscany vineyard.<br \/>But things change. And things can change really fast today at the battlefront of fronted development (loudly playing &#8217;70s Battlestar Galactica soundtrack here).<\/p>\n\n\n\n<p>Furthermore, I do not claim to be an expert neither in Angular, nor in Vue (or in javascript, for all that it matters). In what follows I am just exposing what I found to be a possible solution to a specific problem I had.<br \/>Maybe it could be helpful to someone else, or maybe someone will address me to a better solution. So here we are.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The problem<\/h3>\n\n\n\n<p>We&#8217;ve got a huge legacy single-page app, five or six years worth of coding in Angular 1.x, whose layout may be schematically represented as in this picture:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/01-simple_app.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>Which, if we break it down into its constituents, mainly results composed of five components:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/02-app_components.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>1. A header, which contains a form to query for something.<br \/>2. A wrapper for a master-detail view.<br \/>3. A sidebar, which displays search results.<br \/>4. A container to display the currently selected detail&#8217;s info.<br \/>5. A sub-component, inside the previous one, to display additional data.<\/p>\n\n\n\n<p>At this point our applications is simply organized according to the following directory structure:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/a9d4f8f027200e90c7e6ad8aa4438564.js\"><\/script><\/p>\n\n\n\n<p>No webpack, transpilation or other module bundling helpers.<br \/>See the codebase in the\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-01-angular-app\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-01-angular-app<\/a><\/code><\/strong>\u00a0tag of the associated repository.<\/p>\n\n\n\n<p>Ideally you will migrate everything to Vue, but you cannot stop implementing new features while rejuvenating. No chances to unplug the app today to plug it in a year from now completely renewed (it could be dangerous or really time consuming). You have to maintain the legacy code, allowing the beasts to communicate, deploy often to reiterate on changes done, and migrate it progressively, step by step, with a little patience, as the poet would say:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Said, woman, take it slow<br \/>It&#8217;ll work itself out fine<br \/>All we need is just a little patience.&#8221;<\/p>\n<cite><em>(Guns N&#8217; Roses)<\/em><\/cite><\/blockquote>\n\n\n\n<p>In the end, for reasons I will not expose here (related to an old architecture and refactoring decisions), what we are going to do, at least as a firs step, could be summarized as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>keep the Angular app alive;<\/li>\n<li>completely rewrite in Vue the component number 2 (the master-detail wrapper) together with its children, components 3 and 4; but&#8230;<\/li>\n<li>recycle Angular child component number 5 (which is too big, too complex to be refactored for the time being).<\/li>\n<\/ul>\n\n\n\n<p>What I am stating is that from an app completely written in Angular 1.x we are moving to this hybrid solution:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/03-ng_vue_components.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>I know what you are thinking. But it happens. And (if you are like me) here the fun begins!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Requirements<\/h3>\n\n\n\n<p>We can hence list the main guidelines which will direct the migration:<\/p>\n\n\n\n<p><strong>1. Support for Vue components inside an Angular app.<\/strong><br \/>Aka: Vue components inside Angular components. This gives us the ability to replace Angular bricks with Vue bricks, avoiding our building to collapse.<\/p>\n\n\n\n<p><strong>2. Support for Angular components inside Vue components.<\/strong><br \/>Aka: vue inside angular inside vue (doh!). Wait a minute: what? I know, it sounds really strange, but better to reign in Hell than serve in Heaven, right? Well, kind of. As we stated above, we still need to maintain something Angular inside the new Vue codebase. Simply no options here.<\/p>\n\n\n\n<p><strong>3. Vuex store, seamlessly shared between Angular and Vue.<\/strong><br \/>We will progressively introduce Vuex as the one source of truth to manage application state.<\/p>\n\n\n\n<p><strong>4. vue-router<\/strong>.<br \/>We would like to introduce client side routing, to facilitate view switching.<\/p>\n\n\n\n<p><strong>5. A module bundler.<\/strong><br \/>We will make use of ES6+ javascript and modules, a CSS preprocessor, and will bundle our transpiled code to include it in the existing application. Webpack at rescue here.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">So what?<\/h3>\n\n\n\n<p>A lot to do, so many things to understand and to fit into each other.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Me and my brother Vue here,<br \/>We was hitchhikin&#8217; down a long and lonesome road.<br \/>All of a sudden, there shined a shiny demon.&#8221;<\/p>\n<cite><em>(Tenacious D)<\/em><\/cite><\/blockquote>\n\n\n\n<p><a href=\"https:\/\/github.com\/ngVue\/ngVue\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">ngVue<\/a>\u00a0enters here.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;ngVue is an Angular module that allows you to develop\/use Vue components in AngularJS applications.&#8221;<\/p>\n<cite><em>(<a href=\"https:\/\/github.com\/ngVue\/ngVue\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">ngVue repo<\/a>)<\/em><\/cite><\/blockquote>\n\n\n\n<p>Cool: I am a really bad swimmer, but at least a bridge exists. I can write a Vue component and include it into the existing Angular application. That&#8217;s a good start.<br \/>Angular, Vue, ngVue (and Webpack). The Three Musketeers!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Enlightening the path<\/h3>\n\n\n\n<p>The Alien movie teaches us that the best way to generate a new creature is incubating it from the inside.<br \/>To avoid side effects, I would like to preserve things as they are, as much as possible. I would like to isolate the source code I am going to add, and transpile it in a form I can use into the existing.<br \/>So i create a nest for Vue in the form of a new folder, let&#8217;s call it\u00a0<code>vueApp<\/code>:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/b1acd6ab099c7c45b7a338aa0ba2c2f8.js\"><\/script><\/p>\n\n\n\n<p>Ideally the\u00a0<code>vueApp<\/code>\u00a0folder will contain everything related to the migration: Vue code, Vue-Angular temporary hybrid code, Webpack and package.json configurations, node_modules, and the final &#8220;production ready dist&#8221; byproduct.<br \/>Furthermore, I want to keep Vue and hybrid code separated, to be able to delete no more useful Angular code in the future. For a similar reason, I create a\u00a0<code>DEV<\/code>\u00a0folder also, which contains mockups or everything useful to webpack-dev-server only. Adding a bunch of styles assets we then finally come to a development ready directory structure, which, in the end, will be similar to the following:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/6d818a42528a251d1fdfedfa8dc39cce.js\"><\/script><\/p>\n\n\n\n<p><strong>Please note<\/strong>: here I will not initialize the Vue app through vue-cli. I am reusing a Webpack custom configuration which suites my needs. Nevertheless, everything should work the same way if you are using vue-cli.<\/p>\n\n\n\n<p>See tag\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-02-app-directory-structure\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-02-app-directory-structure<\/a><\/code><\/strong>\u00a0(with empty folders and files).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">First things first: setting up Webpack and NPM dependencies<\/h3>\n\n\n\n<p>Let&#8217;s start by &#8220;emulating&#8221; an Angular app to re-create an environment to make quick development iterations before injecting the code into the real app. For sure, this is a contrived example which delineates the way I dealt with my problem: as stated above, in the original Angular app I have got no support from Webpack (or other module bundlers), and ideally I do not want to modify in any way the existing codebase.<br \/>By bootstrapping a dev environment with modern tools I can instead quickly write and test new Vue code and Angular-Vue interactions through webpack-dev-server. Please refer to\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-03-bootstrapping-dev-angular-app\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-03-bootstrapping-dev-angular-app<\/a><\/code><\/strong>\u00a0for a detailed view of the Webpack config files and NPM dependencies (I am using Webpack 4 here).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Webpack config<\/h3>\n\n\n\n<p>Before we start, a few points to note.<br \/>Let&#8217;s begin with\u00a0<code>webpack.config.js<\/code>\u00a0file.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Dev and &#8220;library&#8221; mode<\/h4>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/e18366d2f80ba3192adda145630b9fe1.js\"><\/script><\/p>\n\n\n\n<p>We will initially build our components inside the\u00a0<code>DEV<\/code>\u00a0folder, taking advantage of our testing environment. During development hence, the main entry file will be\u00a0<code>DEV\/dev.index.js<\/code>, and the generated javascript will be injected into\u00a0<code>index.html<\/code>\u00a0page.<br \/>When will switch to the real production build, we will build the codebase as a javascript bundle to include in the existing Angular app, exactly as we would include a new library, and the main entry point will then be\u00a0<code>index.js<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The production build<\/h4>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/42b8bb5a8f74e0e6438561deac3b580a.js\"><\/script><\/p>\n\n\n\n<p>Here we are in essence telling Webpack to generate three files in the final build:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>appVueLib_VendorsDependencies.js<\/strong>: a file to include all vendors dependencies (like vue, vuex, vue-router&#8230;).<\/li>\n<li><strong>appVueLib_NgVueBridge.js<\/strong>: a bundle which contains the &#8220;hybrid&#8221; code required to temporary integrate Angular and Vue. Virtually, once the migration is complete, this code could be completely removed, and the generated file simply will exist no more. We will work on this folder later.<\/li>\n<li><strong>appVueLib.js<\/strong>: the &#8220;real porting&#8221;, the new code completely written in Vue.<\/li>\n<\/ul>\n\n\n\n<p>Those are the files we will include into the existing old Angular app.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Angular as a global object<\/h4>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/40d05a8a250cf5a1511fb88a4e769361.js\"><\/script><\/p>\n\n\n\n<p>The old app already depends on Angular, which is included as an old script tag.<br \/>Hence, to allow the new bundles to access things defined by other javascript on the page, avoid duplication in the build process, and duplication warnings at runtime, we take advantage of\u00a0<a href=\"https:\/\/webpack.js.org\/configuration\/externals\/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Webpack externals<\/a>. The\u00a0<code>angular<\/code>\u00a0dependency is supposed to be already present in the consumer&#8217;s environment.<br \/>Again, we will make use of it later on.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">package.json<\/h3>\n\n\n\n<p>In the package.json are listed all the NPM dependencies we will use now and later (like ngVue or vuex).<br \/>The only thing to note here I will use ES6 to write angular code, so I will take advantage of the\u00a0<a href=\"https:\/\/github.com\/schmod\/babel-plugin-angularjs-annotate\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">babel-plugin-angularjs-annotate<\/a>\u00a0to solve dependency injection. In ES6 code you will find the\u00a0<code>\/** @ngInject *\/<\/code>\u00a0decorator. From you terminal, go to the vueApp folder and run install:<\/p>\n\n\n\n<p><code>cd code\/vueApp\/<\/code><br \/><code>npm install<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A new beginning: setting up a dev app<\/h3>\n\n\n\n<p>All the pieces are now in place to begin the real work. Let&#8217;s start from the\u00a0<code>DEV<\/code>\u00a0folder.<br \/>Create an\u00a0<code>AngularAppWrapper<\/code>\u00a0to host our fake Angular app. At the end this will be the structure of the\u00a0<code>DEV<\/code>\u00a0folder:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/08a7f27b552ac755a51dbf14ed133c8c.js\"><\/script><\/p>\n\n\n\n<p>We will use ES6 to write the angular component (ES6 syntax could also facilitate a porting from old angular codebase to a complete rewriting in Vue):<\/p>\n\n\n\n<p><strong>DEV\/AngularAppWrapper\/index.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/d65a809446f7a07694d8d416aa11ae56.js\"><\/script><\/p>\n\n\n\n<p>whose template is so simple as:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/12c061e0cd84bcab4538723fd0442363.js\"><\/script><\/p>\n\n\n\n<p>And let&#8217;s use it into our development Angular app:<\/p>\n\n\n\n<p><strong>DEV\/dev.index.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/9ad61174b96612ba4e349a205304ab06.js\"><\/script><\/p>\n\n\n\n<p>Now from you terminal launch<\/p>\n\n\n\n<p><code>npm run\u00a0dev<\/code><\/p>\n\n\n\n<p>Nice! A simple Angular app on which experiment with our migration.<br \/>Again, refer to the\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-03-bootstrapping-dev-angular-app\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-03-bootstrapping-dev-angular-app<\/a><\/code><\/strong>\u00a0for everything done so far.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Enters ngVue<\/h3>\n\n\n\n<p>Let&#8217;s now create and use our first Vue-inside-Angular component. To do that we will ask for help to ngVue (refer to the\u00a0<a href=\"https:\/\/github.com\/ngVue\/ngVue\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">official ngVue documentation<\/a>\u00a0for more info).<br \/>I will begin by defining a new Angular module to contain everything related to ngVue.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/46df64f777080339acd3b6e540e5db7d.js\"><\/script><\/p>\n\n\n\n<p>We are simply creating a new Angular module, using, as dependencies, &#8216;ngVue&#8217; and &#8216;ngVue.plugins&#8217; (we will use\u00a0<a href=\"https:\/\/github.com\/ngVue\/ngVue\/blob\/master\/docs\/plugins.md\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">ngVue plugins<\/a>\u00a0later, with vuex, for instance). Basically, this will be the namespace to contain &#8220;angularized&#8221; Vue code.<br \/>Ok, time to create our first Vue component.<\/p>\n\n\n\n<p>Let&#8217;s define a component for a simple app navigation. Note I am using the\u00a0<code>vueCode<\/code>\u00a0folder here, because I am writing a fresh component completely in Vue, to replace existing Angular code. Contrarily to\u00a0<code>DEV<\/code>\u00a0and\u00a0<code>ngVueBridgeCode<\/code>\u00a0folders, which will be eventually deleted, the\u00a0<code>vueCode<\/code>\u00a0one contains the real final migration.<\/p>\n\n\n\n<p><strong>vueCode\/components\/VueAppContainer.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/1fb3534c6ce02d9107f7dbacf9e72812.js\"><\/script><\/p>\n\n\n\n<p>Now, if you simply include this new Vue component inside the AngularAppContainer it will be ignored.<\/p>\n\n\n\n<p><strong>DEV\/AngularAppContainer\/index.html<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/eccbe6532d63e26b4f73f73602ca1a47.js\"><\/script><\/p>\n\n\n\n<p>You have to tell Angular to render this component through ngVue.<br \/>Let&#8217;s create a file to &#8220;transform&#8221; Vue components into Angular ones. With,\u00a0<code>ngVueDirectives.js<\/code>\u00a0we are telling Angular, through ngVue, that our Vue components exist. Again,\u00a0<code>ngVueDirectives.js<\/code>\u00a0is only a temporary bridge file, so we will put it inside\u00a0<code>ngVueBridgeCode<\/code>\u00a0folder.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueDirectives.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/9f997916047fa4aa25fdb93fcf2399d2.js\"><\/script><\/p>\n\n\n\n<p>We are using ngVue&#8217;s\u00a0<code>createVueComponent<\/code>\u00a0factory to translate a Vue component into an Angular directive.<\/p>\n\n\n\n<p>As a first step, we inform the main angular module of the existence of our angularized-vue-components, so inside\u00a0<code>dev.index.js<\/code>\u00a0replace<\/p>\n\n\n\n<p><code>angular.module('ngVueApp', []);<\/code><\/p>\n\n\n\n<p>with<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/9645492adae324aeeaa98795482f11e0.js\"><\/script><\/p>\n\n\n\n<p>et voil\u00e0: your first Vue component inside Angular!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/04-vue_component_inside_angular.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>Basically, we have just fulfilled requirements #1 and #5: we can write new components in Vue, include them into the existing Angular application, and use modern development and bundling tools.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Back to the real: linking old and new applications<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Where we&#8217;re going, we don&#8217;t need roads.&#8221;<\/p>\n<cite><em>(Dr. Emmett Brown, Back to the Future)<\/em><\/cite><\/blockquote>\n\n\n\n<p>But, to say it all, we have to leave our safe development environment, take off, and use the new component inside the real application.<\/p>\n\n\n\n<p>Add to\u00a0<code>index.js<\/code>\u00a0the dependencies required:<\/p>\n\n\n\n<p><strong>vueApp\/src\/index.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/5e868d8c1be28a77e605e1a3e912efd0.js\"><\/script><\/p>\n\n\n\n<p>go to your terminal and run<\/p>\n\n\n\n<p><code>npm run build<\/code><\/p>\n\n\n\n<p>What you get is a\u00a0<code>vueApp\/dist<\/code>\u00a0folder which contains the following files:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/4be37e51f2f2f21828421cf720d144d8.js\"><\/script><\/p>\n\n\n\n<p>This is exactly the &#8220;lib&#8221; we were looking for to enhance our existing Angular application.<br \/>In the main\u00a0<code>index.html<\/code>, include those files and use the new Angular directive:<\/p>\n\n\n\n<p><strong>code\/index.html<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/a89860f01f1e10bfd4e7944e19bcbdd6.js\"><\/script><\/p>\n\n\n\n<p>And do not forget to inform Angular a new module for Vue components exists:<\/p>\n\n\n\n<p><strong>code\/angularApp\/angularApp.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/a808e44870e558269105d4514a794430.js\"><\/script><\/p>\n\n\n\n<p>Et voil\u00e0, it simply works:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/05-vue_component_inside_real_app.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>As a reference, see\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-04-vue-component-inside-real-app\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-04-vue-component-inside-real-app<\/a><\/code><\/strong>.<\/p>\n\n\n\n<p>If you are curious, yes, you can also pass props:<\/p>\n\n\n\n<p><strong>code\/index.html<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/f0501633401369b37bc6442b2a9accd5.js\"><\/script><\/p>\n\n\n\n<p><strong>code\/vueApp\/src\/vueCode\/components\/VueAppContainer.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/705593bd33a00693d67f88b71eb8ff68.js\"><\/script><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;You still don&#8217;t understand what you&#8217;re dealing with, do you? Perfect organism. Its structural perfection is matched only by its hostility.&#8221;<\/p>\n<cite><em>(Ash, Alien)<\/em><\/cite><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">A simple client routing: Vue global plugins<\/h3>\n\n\n\n<p>One of the reasons we started this journey was to replace the master-detail component in the Angular application. So far we have seen how easy is to use a Vue component inside Angular. Let&#8217;s now introduce a little bit of client routing through the\u00a0<code>vue-router<\/code>\u00a0module. This will give us the opportunity to use the\u00a0<code>$ngVue<\/code>\u00a0factory from\u00a0<code>ngVue.plugins<\/code>, and analyze how to define\u00a0<a href=\"https:\/\/github.com\/ngVue\/ngVue\/blob\/master\/docs\/plugins.md#Root-Vue-instance-props\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">root Vue instance properties<\/a>.<\/p>\n\n\n\n<p>Let&#8217;s start by defining a simple router file.<\/p>\n\n\n\n<p><strong>vueCode\/router.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/5f3488d978badccc185a6a74ffe4e522.js\"><\/script><\/p>\n\n\n\n<p><code>vueCode\/components\/Detail\/index.vue<\/code>\u00a0is a simple replacement for the existing detail view.<\/p>\n\n\n\n<p>Then, in the container, empty the\u00a0<code>main<\/code>\u00a0tag and append a\u00a0<code>router-view<\/code>\u00a0component:<\/p>\n\n\n\n<p><strong>vueCode\/components\/VueAppContainer.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/0d816a087660f52b110c42f7a0d8c562.js\"><\/script><\/p>\n\n\n\n<p>Usually, in a Vue application, you would pass the store as a property to the root Vue instance. Something like:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/2be1c208b5a1fa7b4e1eb01d0e8075ac.js\"><\/script><\/p>\n\n\n\n<p>But here, in the context of Angular\/ngVue this will not work. We have to use\u00a0<code>$ngVueProvider<\/code>\u00a0at the configuration phase of Angular module to inject the property.<br \/>Again, I will configure it in the\u00a0<code>ngVueComponentsModule<\/code>, because there lives everything related to ngVue.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/2dd5f15ac23f931843cac21ef7fd72ac.js\"><\/script><\/p>\n\n\n\n<p><code>vue-router<\/code>\u00a0is now enabled, and you can access it on any child component: we have just fulfilled requirement #4.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>What most people don&#8217;t understand is that UFOs are on a cosmic tourist route. That&#8217;s why they&#8217;re always seen in Arizona, Scotland, and New Mexico. Another thing to consider is that all three of those destinations are good places to play golf. So there&#8217;s possibly some connection between aliens and golf.<\/p>\n<cite><em>(Alice Cooper)<\/em><\/cite><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Sharing factories: consuming Angular services from Vue<\/h3>\n\n\n\n<p>Actually we still lack one piece: router links. To add them we will refactor our code a little bit. Even though we will soon replace it with something Vuex, refactoring routing give us the opportunity to rewrite the existing\u00a0<code>searchService.js<\/code>, and transform it in something both Angular and Vue can consume (and this could be useful in many situations).<\/p>\n\n\n\n<p>Let&#8217;s start by rewriting it in ES6 into the\u00a0<code>ngVueBridgeCode\/services<\/code>, to transform it into something &#8220;less Angular&#8221;.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/services\/searchService.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/ad6d4d5a14734a20e7f6904d9cbd54db.js\"><\/script><\/p>\n\n\n\n<p>Our service is a plain javascript class. In the future we will simply import and use it as a ES module in Vue code. For now, we will share it on Angular and Vue instances thanx to\u00a0<a href=\"https:\/\/docs.angularjs.org\/api\/auto\/service\/$provide#service\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Angular&#8217;s providers<\/a>\u00a0and the\u00a0<a href=\"https:\/\/docs.angularjs.org\/api\/auto\/service\/$injector\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">$injector service<\/a>.<br \/>An angular\u00a0<code>service<\/code>\u00a0registers a service constructor, invoked with\u00a0<code>new<\/code>, to create the service instance. It should be used (guess what) when we define the service as a class.<br \/><code>$injector<\/code>\u00a0is an Angular service used to retrieve object instances as defined by a provider.\u00a0<code>$injector.get<\/code>\u00a0returns the instance of the service.<br \/>Exporting an instance of an Angular service allow then us to import and use it anywhere.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;My dear, here we must run as fast as we can, just to stay in place. And if you wish to go anywhere you must run twice as fast as that.&#8221;<\/p>\n<cite><em>(Alice in Wonderland)<\/em><\/cite><\/blockquote>\n\n\n\n<p>Add this code to\u00a0<code>ngVueComponentsModule.js<\/code>:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/31142f65b88ab987e3f4b46b1ba28512.js\"><\/script><\/p>\n\n\n\n<p>and we are done:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>we have rewritten the service as a class (previous code snippet)<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>instantiated it as an Angular service (comment #1)<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>exported the instance through\u00a0<code>searchService<\/code>\u00a0(comment #2).<\/li>\n<\/ul>\n\n\n\n<p><strong>A note<\/strong>: to simplify a little bit, I deleted the\u00a0<code>ngVueDirectives.js<\/code>\u00a0file from\u00a0<code>ngVueBridge<\/code>\u00a0folder, and move the code there directly into\u00a0<code>ngVueComponentsModule<\/code>\u00a0(remove also the import inside\u00a0<code>vueApp\/src\/index.js<\/code>\u00a0e\u00a0<code>vueApp\/src\/DEV\/dev.index.js<\/code>). Refer to the codebase in\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-05-vue-globals\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-05-vue-globals<\/a><\/code><\/strong>.<\/p>\n\n\n\n<p>Thanx to point 2 above you can safely delete\u00a0<code>angularApp\/services\/searchService.js<\/code>\u00a0(and the script tag inside\u00a0<code>index.html<\/code>). You can leave the existing Angular code untouched, and everything will keep working (remember to\u00a0<code>npm run build<\/code>).<br \/>Move on and migrate also &#8220;detail&#8221; and &#8220;searchResults&#8221; components. Here, we can barely mimic the existing code with little effort.<\/p>\n\n\n\n<p><strong>vueCode\/components\/SearchResults\/index.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/5b631598f6a438862654aa6648c3c997.js\"><\/script><\/p>\n\n\n\n<p>The HTML template is quite the same (the only difference being the use of a routing system). You can also simply copy and paste the css code from\u00a0<code>style.css<\/code>.<br \/>And magic:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/66d691a0adb2cd7e0f225f369afbb00a.js\"><\/script><\/p>\n\n\n\n<p>we are importing and using the\u00a0<code>searchService<\/code>\u00a0previously instantiated.<\/p>\n\n\n\n<p>Basically\u00a0<code>vueCode\/components\/Detail\/index.vue<\/code>\u00a0works exactly as\u00a0<code>SearchResults<\/code>\u00a0(refer to the repo).<br \/>Complete the refactor simplifying the container:<\/p>\n\n\n\n<p><strong>vueCode\/components\/VueAppContainer.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/df17af8cb11c9156a6ccded1c2e26106.js\"><\/script><\/p>\n\n\n\n<p>add the component to\u00a0<code>index.html<\/code>, and rebuild:<\/p>\n\n\n\n<p><strong>code\/index.html<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/07049023091d37f32b30f1d7743e4f40.js\"><\/script><\/p>\n\n\n\n<p>We have just doubled (and almost completely migrated) our dear old Angular code:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/06-vue_angular_duplicate.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>Starting a search (Angular component) will now activate Vue components. You can safely delete all the related dead Angular code.<br \/>Cool! We have just migrated to Vue a huge part of our application.<br \/>For details, refer to\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-05-vue-globals\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-05-vue-globals<\/a><\/code><\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A centralized store: Vuex<\/h3>\n\n\n\n<p>In some way, we are using the\u00a0<code>searchService<\/code>\u00a0as a sort of centralized store to manage all our search and routing needs. We can do better, replacing part of or completely remove it, and introduce\u00a0<a href=\"https:\/\/vuex.vuejs.org\/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Vuex<\/a>\u00a0as a more advanced, performant, and predictable state manager.<br \/>Let&#8217;s start by adding a basic store file.<\/p>\n\n\n\n<p><strong>vueCode\/store.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/1b06801bd9c16aaa2a6e186ef08c7279.js\"><\/script><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;A journey of a thousand miles must begin with the first step.&#8221;<\/p>\n<cite><em>(Lao Tzu)<\/em><\/cite><\/blockquote>\n\n\n\n<p>One day we will let the store manage everything. Now we only need one single small step (for a man).<br \/>No, we are not sending our application to the Moon (even though sometimes we would like to). We want to move to the store only the code responsible for retrieving and store search results.<\/p>\n\n\n\n<p>As for vue-router previously, we have to add the store to the global properties.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/25691832c1d89300dd815cec47f1357d.js\"><\/script><\/p>\n\n\n\n<p>But, wait a minute: and now what? Angular service and Vuex are separated worlds, how can they communicate?<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;What we&#8217;ve got here is failure to communicate.&#8221;<\/p>\n<cite><em>(The Captain, Cool Hand Luke)<\/em><\/cite><\/blockquote>\n\n\n\n<p>Well, Vuex is simply a JavaScript object that stores data, isn&#8217;t it?<br \/>I admit I was stumbling on my way to nowhere for a while, desperately searching for a solution, until I ran into it thanx to the suggestion given by a couple of sentences in\u00a0<a href=\"https:\/\/cushionapp.com\/journal\/vuejs-inside-angularjs\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">How to embed Vue.js &amp; Vuex inside an AngularJS app&#8230; wait what?<\/a>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;In Angular, there are providers, which are by far the most confusing aspect of Angular. [&#8230;] One of these providers is called a \u201cservice\u201d, which can be used to create a single store to reference throughout the app. All it needs is a function that returns an object. With a single line of code, I can return Vuex as an Angular service.&#8221;<\/p>\n<cite><em>(Jonnie Hallman)<\/em><\/cite><\/blockquote>\n\n\n\n<p>Really intriguing! The solution is criptically dug there (in clear). Read that, and read it again; lucubrate, my little brain; use the Rosetta Stone to decipher Angular&#8217;s documentation for\u00a0<a href=\"https:\/\/docs.angularjs.org\/guide\/providers\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">providers<\/a>.<br \/>The keys here are factory and service recipes.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>JavaScript developers often use custom types to write object-oriented code.<\/p>\n<\/blockquote>\n\n\n\n<p>Yes, it&#8217;s me.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The Factory recipe can create a service of any type, whether it be a primitive, object literal, function,\u00a0<strong>or even an instance of a custom type<\/strong>.<\/p>\n<\/blockquote>\n\n\n\n<p>For example:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/05a54c19fd7bc1c446d21d86457851f8.js\"><\/script><\/p>\n\n\n\n<p>Remember? From\u00a0<code>store.js<\/code>\u00a0we are exporting\u00a0<code>new Vuex.Store()<\/code>.<br \/>And then, all of a sudden: EUREKA! Add a single line of code.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/683024faedecc5c39227b6cac160ccab.js\"><\/script><\/p>\n\n\n\n<p>Brilliant! We have just exposed our store instance as an Angular service. Thanx Jonnie.<br \/>To consume it, just inject it into the constructor of\u00a0<code>searchService.js<\/code>, replace code in\u00a0<code>executeQuery<\/code>, and use it exactly as you would in Vue:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/services\/searchService.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/e902bd181fbee925803c46720a20c6c8.js\"><\/script><\/p>\n\n\n\n<p>Could you see the potential? You can progressively migrate your services.<br \/>And you can use it also inside your dear plain old Angular components. For example, let&#8217;s say we would like to add a results counter in the header:<\/p>\n\n\n\n<p><strong>angularApp\/components\/search.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/45ac5fdf8137ff204977f7194e5fb1e0.js\"><\/script><\/p>\n\n\n\n<p><strong>Opinionated tip<\/strong>: If you inject the service renaming it\u00a0<code>$store<\/code>\u00a0you got something very Vue:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/25b4c4aea82e63a8ca6f0c359fdf9d51.js\"><\/script><\/p>\n\n\n\n<p>The\u00a0<code>resultsCount<\/code>\u00a0function to me is like simulating a Vue&#8217;s computed property (ok, just a ton heavier).<br \/>But, if you rebuild, launch the application, and start a search&#8230; WTF? No count!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/07-vuex_in_angular_component.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>As you know, we are crashing here with the mysterious world of\u00a0<a href=\"https:\/\/docs.angularjs.org\/guide\/scope#scope-life-cycle\" target=\"_blank\" rel=\"noopener noreferrer nofollow\"><strong>Angular&#8217;s digest loop<\/strong><\/a>. We are doing something secretly from Angular. We explicitly need to call Angular, and inform it something has changed to trigger the digest. Yes:\u00a0<code>$apply<\/code>\u00a0at rescue here.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Isn&#8217;t it unsafe to travel at night? It&#8217;ll be a lot less safe to stay here&#8230; Is there anybody out there?&#8221;<\/p>\n<cite><em>(Pink Floyd)<\/em><\/cite><\/blockquote>\n\n\n\n<p><strong>ngVueBridgeCode\/utilities\/safeApply.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/139211bb8a0171e21f941b4c9348a281.js\"><\/script><\/p>\n\n\n\n<p>I am wrapping the function in a &#8220;safe apply&#8221; to avoid possible &#8220;$apply already in progress&#8221; errors. But how to use it? One of many possible solutions follows.<br \/>If you rewrite your component as an ES6 class you can simply import and use it as a module. Here we are still dealing with a classic component, so I will write a service as a proxy to expose it:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/services\/utilities.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/8e6ed2f8cf800085f06631dd5bc34e85.js\"><\/script><\/p>\n\n\n\n<p>and<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/db800761ffba25b3173f35a8d138ba9b.js\"><\/script><\/p>\n\n\n\n<p>No need to export anything here, we will not use it inside Vue code.<br \/>A possible use of that is (also thanx to the fact we are using promises):<\/p>\n\n\n\n<p><strong>angularApp\/components\/search.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/518b21c37ce633fcdaae658d74dbe194.js\"><\/script><\/p>\n\n\n\n<p>In practical terms, we are manually invoking a rendering. We are introducing a maybe unnecessary\u00a0<code>$scope<\/code>, but that&#8217;s a small price to pay. Now, if you start a search, you will get a working counter.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/08-safe_apply.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>There are however situations in which you cannot use this approach, or maybe you want to achieve something more complex, or let Angular and Vue communicate passing data each other.<\/p>\n\n\n\n<p>As a stupid example, think to a button inside the Vue component responsible to render a selected detail. Clicking the button you add a new item to the results set. I know, a really stupid example, but quite useful in demonstrating what I am going to expose.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/09-event_bus.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>Let&#8217;s start by adding the button in the component template:<\/p>\n\n\n\n<p><strong>vueCode\/components\/Detail\/index.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/922286248a84590a2b2bf61b18a7bb25.js\"><\/script><\/p>\n\n\n\n<p>And the relative action in the store:<\/p>\n\n\n\n<p><strong>vueCode\/store.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/f67f575316337182b196c323393190d9.js\"><\/script><\/p>\n\n\n\n<p>If you test this code now you can easily see that, when you press the button, nothing happens. Actually nothing happens until you wake Angular up: try pressing the &#8220;Add result&#8221; button and then change the search string in the input, or press the &#8220;Search&#8221; button again.<br \/>No black magic here, you are just letting Angular digest the pizza.<\/p>\n\n\n\n<p>There are many possible alternative solutions.<br \/>For example, while I was studying the problem I came to this smart jQuery solution:\u00a0<a href=\"https:\/\/medium.com\/unbabel\/progressively-migrating-from-angularjs-to-vue-js-at-unbabel-581eb4ae022d\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Progressively migrating from AngularJS to Vue.js at Unbabel<\/a>. There the author suggests, if you are already including it, to use jQuery as a bridge, or, better, as an\u00a0<strong>event bus<\/strong>, taking advantage of its\u00a0<code>.trigger()<\/code>\u00a0and\u00a0<code>.on()<\/code>\u00a0methods, to trigger custom events, and share information between Angular and Vue.<br \/>Sure, a possibility. But can we replicate that in a cleaner way, where clean means &#8220;Vue as much as possible&#8221;? After all it would be nice to remove another additional dependency.<br \/>Well, maybe we can, thanx to the possibility of creating a global event bus in Vue (refer to the official documentation about\u00a0<a href=\"https:\/\/vuejs.org\/v2\/guide\/state-management.html#Simple-State-Management-from-Scratch\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">state management<\/a>, or\u00a0<a href=\"https:\/\/alligator.io\/vuejs\/global-event-bus\/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">here<\/a>\u00a0and\u00a0<a href=\"http:\/\/vuetips.com\/global-event-bus\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">here<\/a>).<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/utilities\/vueAngularEventBus.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/01919157819ad8261a718cb020a7cd06.js\"><\/script><\/p>\n\n\n\n<p>Simple as is.<br \/>As usual, to use it in Angular wrap the Vue instance returned above into a factory:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/a48965aca93f6baf30bd44d59039da47.js\"><\/script><\/p>\n\n\n\n<p>I am placing it into\u00a0<code>ngVueBridgeCode<\/code>\u00a0folder because it is just a temporary helper, and ideally it will be removed once the migration is complete. I will simply delete it and all references to\u00a0<code>VueAngularEventBus<\/code>.<\/p>\n\n\n\n<p>Using it is very simple. Remember: what we are going to do is to inform Angular to re-render because something has changed somewhere out there. Namely: when a Vue component updates the store simultaneously the store fires Angular to trigger a new $digest cycle.<\/p>\n\n\n\n<p>Therefore, we need a reference to the bus in the store:<\/p>\n\n\n\n<p><strong>vueCode\/store.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/7a5e2ab8fe3e82095f4446a14aa0320b.js\"><\/script><\/p>\n\n\n\n<p>On committing a mutation we will also emit an event through the bus. All the listeners subscribed will react consequently:<\/p>\n\n\n\n<p><strong>angularApp\/components\/search.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/99078961b871013c70f63c2cfa5fc0e7.js\"><\/script><\/p>\n\n\n\n<p>Which we could represent graphically as:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/10-event_bus_triggers.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>The component re-renders anytime the custom &#8220;result-added&#8221; event is triggered. As a consequence, the counter will be now updated every time you press the &#8220;Add result&#8221; button.<br \/>Remember to remove the listener once you destroy the component.<\/p>\n\n\n\n<p>Completing that we have also shipped requirement #3.<br \/>Refer to\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-06-using-vuex\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-06-using-vuex<\/a><\/code><\/strong>\u00a0for what we have done so far.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;See this? This is my boom stick! It&#8217;s a 12-gauge, double-barreled Remington. S-mart&#8217;s top of the line. You can find this in the sporting goods department. [&#8230;] It&#8217;s got a walnut stock, cobalt blue steel and a hair trigger.&#8221;<\/p>\n<cite><em>(Ashley J. Williams, Army of Darkness)<\/em><\/cite><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Bonus #1: free Angular components from Angular<\/h3>\n\n\n\n<p>A further step in the migration could be rewriting existing Angular components as ES6 modules. You can move them into your webpack build, you can write them in a more concise style, you are maybe learning ES6+ and want to have fun&#8230; whatever. Or maybe you are not interested in any restyling (you already write Angular component that way or you prefer to directly migrate the component to Vue). Either is fine.<br \/>Just in case, you can move for example\u00a0<code>angularApp\/components\/search.js<\/code>\u00a0into\u00a0<code>vueApp\/src\/ngVueBridgeCode\/components\/Search\/index.js<\/code>, and rewrite it as:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/components\/Search\/index.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/c40da4d5416cb74ef65142a7a6446760.js\"><\/script><\/p>\n\n\n\n<p><strong>Note<\/strong>: to use\u00a0<code>SafeApply<\/code>\u00a0we do not need the wrapping utilities service anymore.<\/p>\n\n\n\n<p>Instantiate it as an Angular component:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/ngVueComponentsModule.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/5cf9216739467a5eb00396ea572b52ce.js\"><\/script><\/p>\n\n\n\n<p>And delete all files and code related to the original component. Our Angular app is reducing to its bare bones.<br \/>See\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-07-es6-components\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-07-es6-components<\/a><\/code><\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Bonus #2: free Vue components from Angular services<\/h3>\n\n\n\n<p>Now we have got a store we can move on and strip\u00a0<code>searchService<\/code>\u00a0of unnecessary parts.<br \/>For example, the\u00a0<code>store.currentDetail<\/code>\u00a0property and the\u00a0<code>selectItem(id)<\/code>\u00a0method are exclusively used by the\u00a0<code>detail<\/code>Vue component. Let&#8217;s move them from the Angular service to the Vuex store.<\/p>\n\n\n\n<p>Comment (or delete) the following lines:<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/services\/searchService.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/77d2da25c07343387ddf38231eb80fe3.js\"><\/script><\/p>\n\n\n\n<p>And modify the store:<\/p>\n\n\n\n<p><strong>vueCode\/store.js<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/ae04271cfd71c2f0a4798388cf35dca7.js\"><\/script><\/p>\n\n\n\n<p>Lastly, rewrite the component to use the store in place of the service:<\/p>\n\n\n\n<p><strong>vueCode\/components\/Detail\/index.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/daaa20ea2170b1776dc071a39d939dfe.js\"><\/script><\/p>\n\n\n\n<p>Sure, you can probably do the same with the\u00a0<code>store.searchResults<\/code>\u00a0property. Our Angular app is reducing to its bare bones.<br \/>Refer to\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-08-more-store\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-08-more-store<\/a><\/code><\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusions<\/h3>\n\n\n\n<p>As you have seen, once you grasp a way (I am not claiming here mine is the best or the only one) to let Angular 1.x and Vue cohabit things get easier, and you can resort to a methodology for migrating your codebase progressively.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;I belong to the warrior in whom the old ways have joined the new.&#8221;<\/p>\n<cite><em>(The Last Samurai)<\/em><\/cite><\/blockquote>\n\n\n\n<p>Again, what has been exposed in this article reflects only my opinions, and do not, in any way, constitute the best or only way to achieve the ultimate goal of renewing an old application by completely removing Angular code.<\/p>\n\n\n\n<p>Oh, one more thing&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angular components nested inside Vue components<\/h3>\n\n\n\n<p>Ok, you got me! What about requirement #2? What happened to\u00a0<code>inner-detail<\/code>\u00a0once you migrated to\u00a0<code>vue-app-container<\/code>?<br \/>I have to be honest here, and admit we must be brave and really creative to solve the last puzzle.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;That&#8217;s my friend, Irishman. And the answer your question is yes &#8211; if you fight for me, you get to kill the Angular.&#8221;<\/p>\n<cite><em>(William Wallace, Braveheart)<\/em><\/cite><\/blockquote>\n\n\n\n<p>As confirmed by one of the main repository contributors, ngVue was not designed to allow AngularJS components to be rendered inside Vue components. Someone has tried to solve the problem using\u00a0<code>slots<\/code>, but, due to rendering differences between the frameworks, the implementation is buggy (and not recommended, because maybe in the future will be deprecated, as stated by\u00a0<a href=\"https:\/\/github.com\/ngVue\/ngVue\/issues\/66\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">issue #66<\/a>).<br \/>After a brief discussion (see\u00a0<a href=\"https:\/\/github.com\/ngVue\/ngVue\/issues\/79\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">issue #79 on GitHub<\/a>), thanx to tips coming from all the participants involved (and a previous experience with Angular\u00a0<code>injector<\/code>), I overcame the problem the way I will tell below.<br \/>It seems to work, but it is somehow experimental indeed (I simply lack a deep knowledge on the subject, and I am not completely aware of possible unwanted side effects). Hence I am not sure I would really recommend it.<br \/>Anyway, to me nesting Angular components inside Vue was an essential requirement; so I report it here to complete the picture, and give a possible solution.<\/p>\n\n\n\n<p><strong>TL;DR<\/strong>: I cooked up a Vue component which wraps and compiles an Angular component, and quietly listen for changes in the scope bound.<\/p>\n\n\n\n<p><strong>ngVueBridgeCode\/components\/AngularComponent.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/2b9620e855a1987610586750664ba0c1.js\"><\/script><\/p>\n\n\n\n<p>Which I could roughly visually summarize as:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/12-vue_angular_interaction.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>A lot of stuff in a few lines:<\/p>\n\n\n\n<p><strong>#1<\/strong>:\u00a0<code>injector<\/code>\u00a0is an Angular object that can be used for retrieving services as well as for dependency injection (see the\u00a0<a href=\"https:\/\/docs.angularjs.org\/api\/ng\/function\/angular.injector\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">official documentation<\/a>). Here we are accessing to it to inject and compile a component on the fly, after the Angular application has already been bootstrapped.<\/p>\n\n\n\n<p><strong>#2<\/strong>: here we are setting the scope we will bind to the component template, extending a fresh\u00a0<code>$scope<\/code>\u00a0with the\u00a0<code>$ctrl<\/code>object passed by the\u00a0<code>component<\/code>\u00a0prop, which basically is an object like this:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/13e4764f866bfc71fe794f556f7e96b7.js\"><\/script><\/p>\n\n\n\n<p><strong>#3<\/strong>: we replace the\u00a0<code>&lt;div\/&gt;<\/code>\u00a0tag in the Vue template with the compiled Angular component.<\/p>\n\n\n\n<p><strong>#4<\/strong>: as already seen previously, we are entangled to Angular $digest loop. To inform Angular something has changed in the object associated to its current scope, update bindings, and re-render, we are introducing a\u00a0<code>watcher<\/code>\u00a0on the\u00a0<code>component.$ctrl<\/code>\u00a0prop. Note the\u00a0<code>{ deep: true }<\/code>\u00a0option, to trigger the watcher in case you have got a complex nested object.<\/p>\n\n\n\n<p><strong>#5<\/strong>: any time the prop changes, we update the scope by merging the new object\u00a0<code>ctrl<\/code>\u00a0with the existing\u00a0<code>scope.$ctrl<\/code>\u00a0&#8211;\u00a0<code>angular.merge<\/code>\u00a0performs a\u00a0<strong>deep copy<\/strong>, which is what we need here to be sure to propagate all the updates.<\/p>\n\n\n\n<p><strong>#6<\/strong>: and any time the prop changes, we call our old friend\u00a0<code>SafeApply<\/code>\u00a0(which works here as a sort of &#8220;render&#8221; function), bound to an updated scope, to start a\u00a0<code>$digest<\/code>.<\/p>\n\n\n\n<p><strong>#7<\/strong>:\u00a0<code>this.$watch<\/code>\u00a0return a function we can use to clear the watcher when the component got destroyed.<\/p>\n\n\n\n<p>Given what the\u00a0<a href=\"https:\/\/docs.angularjs.org\/api\/ng\/type\/$rootScope.Scope#$apply\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">official documentation for\u00a0<code>$apply<\/code><\/a>\u00a0says, it is maybe better to rewrite #5 e #6 as:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/0391ffe013343e80e7f0635b10a78bd5.js\"><\/script><\/p>\n\n\n\n<p>Then you simply use the component wherever you want to inject an Angular component:<\/p>\n\n\n\n<p><strong>vueCode\/components\/Detail\/index.vue<\/strong><\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/201ef84e6d948824750a3aa50abdbf7a.js\"><\/script><\/p>\n\n\n\n<p><code>innerDetail<\/code>\u00a0is the\u00a0<code>component<\/code>\u00a0prop we have previously introduced. It is better to define it as a computed property to get it correctly initialized.<\/p>\n\n\n\n<p><strong>Please note<\/strong>: I have found that in order to have the Angular component completely working, you nee to define its HTML template in a separate file:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/ca0d5433ebd687e23c6f59f779ec6a57.js\"><\/script><\/p>\n\n\n\n<p>I guess it depends on how and when things are getting parsed and compiled.<br \/>For instance, if you write:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/867a155d39251cc3320c93539579ff85.js\"><\/script><\/p>\n\n\n\n<p>things will not completely work, and you end up having on screen an unresolved template:<\/p>\n\n\n\n<p><script src=\"https:\/\/gist.github.com\/arcadeJHS\/f9475262c62bd3430736c9d4900d4e33.js\"><\/script><\/p>\n\n\n\n<p>If you now rebuild and launch the application you can check the\u00a0<code>innerDetail<\/code>\u00a0component is working as expected:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/arcadeJHS\/AngularVueIntegration\/master\/screenshots\/11-angular_component_inside_vue.png\" alt=\"\" title=\"\"><\/figure>\n\n\n\n<p>Refer to\u00a0<strong><code><a href=\"https:\/\/github.com\/arcadeJHS\/AngularVueIntegration\/tree\/tag-09-angular-component-inside-vue\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" aria-label=\" (opens in a new tab)\">tag-09-angular-component-inside-vue<\/a><\/code><\/strong>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>K:<\/strong>\u00a0I hope you don\u2019t mind me taking the liberty. I was careful not to drag in any dirt.<br \/><strong>Sapper Morton:<\/strong>\u00a0I don\u2019t mind the dirt. I do mind unannounced visits.<\/p>\n<cite><em>(Blade Runner 2049)<\/em><\/cite><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>A ridiculously detailed and opinionated attempt to let Angular and Vue peacefully live together.<\/p>\n","protected":false},"author":33,"featured_media":27490,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33],"tags":[79,80,77,78],"class_list":["post-27478","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tech","tag-angular","tag-frontend","tag-javascript","tag-vue"],"_links":{"self":[{"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/posts\/27478","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/comments?post=27478"}],"version-history":[{"count":1,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/posts\/27478\/revisions"}],"predecessor-version":[{"id":27659,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/posts\/27478\/revisions\/27659"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/media\/27490"}],"wp:attachment":[{"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/media?parent=27478"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/categories?post=27478"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/exmachina.ch\/en\/wp-json\/wp\/v2\/tags?post=27478"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}