How to use Vue 2.0 components in an angular application

Jermaine Oppong
4 min readDec 11, 2016

--

We were recently tasked with developing a UI kit to be used as a global style guide across our products. Part of this process involved selecting a framework with data-binding capabilities to be used in the development of our interactive widgets. Fortunately finding one was not much of a challenge seeing the influx of options we have to choose from namely Angular, React, Knockout etc…

Long story short we eventually settled with Vue because of its lightweight source, high-performance and brilliant documentation with easy-to-follow examples. What we needed to ensure however was the flexibility to integrate with other frameworks. Since our web applications are built in Angular, it was paramount that Vue did not cause any major setbacks.

Fortunately this investigation produced desired results and I am going to share with you how to reuse already implemented Vue components in an Angular app without rewriting your components!

1. Grab the source and setup your page

For this tutorial I recommend opening up jsfiddle or plunkr, just to get started. Let’s start by adding our dependencies to our HTML page between the opening and closing <head> element or directly after the opening <body> tag:

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>

2. Prepare our component for use

The component we will use in this case is a slightly-adjusted simple-counter example from the docs below:

// Register our component
Vue.component('simple-counter', {
props: ['initialCounterValue'],
template: '<button v-on:click="increment">{{counter}}</button>',
data: function() {
return {
counter: this.initialCounterValue
}
},
methods: {
increment: function() {
this.counter+=1;
this.$emit('increment', this.counter);
}
}
});

This component when instantiated will render a HTML button. Clicking this button increases the numeric value of the ‘counter’ instance property.

The “methods” property of our component options contains a map of functions that can be referenced during interactions with the widget. This example hosts an “increment” function which is triggered when the button is clicked. This trigger increases the ‘counter’ property value and also emit an event via its $emit instance method. This emitted event will be sent upwards to a Vue root constructor instance to be captured and handled by Angular(more on this later). The “props” property on the constructor contains a list of attributes that are expected to be declared on our component when used in our html, in this case ‘initialCounterValue’:

<simple-counter v-bind:initial-counter-value="0"></simple-counter>

3. Bootstrap our Angular application

Let’s prepare Angular, including a directive that will serve as the point of interaction with our component:

// Register our angular module
angular.module('app', [])
.directive('simpleCounterWrapper', function() {
return {
restrict: 'A',
link: function(scope, $element) {
// Vue component interaction logic goes here
}
}
});
// Start our application with our created module
angular.bootstrap(document.body, ['app']);

Our HTML will look like this:

<div simple-counter-wrapper>
<simple-counter v-bind:initial-counter-value="0"></simple-counter>
</div>

One thing to look out for is Angular attempting to compile any element that looks like a custom directive, in our case simple-counter. To stop Angular from doing this, they provide a directive called ngNonBindable. Let’s update our markup to include this:

<div simple-counter-wrapper>
<simple-counter ng-non-bindable v-bind:initial-counter-value="0"></simple-counter>
</div>

4. Set up our ‘bus’

Vue’s constructor method allows us to create a basic ‘event bus’ system that we will use to handle communication between both frameworks. Inside the link function in our Angular directive, include the below:

// Create our root instance
scope.vue = new Vue({
el: 'simple-counter'
})

At this point, you should see the button rendered on the page. Clicking the button increases the value as expected and emits the ‘increment’ event.

So how do we tell Angular about this event? By creating a method on our root Vue instance and calling this method on our component’s “increment” custom event handler:

// Angular scope property
scope.countValue = 0;
// Create our root instance
scope.vue = new Vue({
el: 'simple-counter',
methods: {
updateAngularScopeProperty: function(value) {
scope.$apply(function() {
scope.countValue = value;
});
}
}
});

And in our html:

<div simple-counter-wrapper>
<simple-counter ng-non-bindable
v-bind:initial-counter-value="0"
v-on:increment="updateAngularScopeProperty"
></simple-counter>
{{countValue}}
</div>

The string expression ‘{{countValue}}’ is a reference to the property on the Angular scope that will be updated when the button is clicked. At this point we have a working example on how we can get Angular and Vue talking to each other. The last thing we can do in this example is allow the initialCounterValue property to be configurable via Angular:

// Angular scope property
scope.countValue = 0;
// Create our root instance
scope.vue = new Vue({
el: 'simple-counter',
data: function() {
return {
initialCounterValue: scope.countValue
}
}
,
methods: {
updateAngularScopeProperty: function(value) {
scope.$apply(function() {
scope.countValue = value;
});
}
}
});

and in our HTML:

<div simple-counter-wrapper>
<simple-counter ng-non-bindable
v-bind:initial-counter-value="initialCounterValue"
v-on:increment="updateAngularScopeProperty"
></simple-counter>
{{countValue}}
</div>

Conclusion

And this concludes our tutorial. Learn more about Vue and how components communicate with each other in their documentation. You can view this example on plunkr. Here’s a fiddle I made earlier.

Please share your thoughts – I’d love some constructive feedback :-)

--

--

Jermaine Oppong

Christian | Web Developer | Egghead.io instructor | Sharing exclusive Dart content on my Blog → https://creativebracket.com | @creativ_bracket