Skip to content

Instantly share code, notes, and snippets.

@BabOuDev
Created September 13, 2019 15:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save BabOuDev/ecf2969413cff8eb492e1cbc5a8d39d7 to your computer and use it in GitHub Desktop.
Save BabOuDev/ecf2969413cff8eb492e1cbc5a8d39d7 to your computer and use it in GitHub Desktop.
AngularJS directive wrapper for VueJS components
import Vue from 'vue'
// the list of vue components to install
import components from './vueComponents'
class SlVueWrapper {
constructor($timeout) {
this.name = 'vue';
this.restrict = 'A';
this.terminal = true;
this.priority = 9999;
this.compile = function directive(elem) {
// prevents angular to update vue content
elem[0].setAttribute('ng-non-bindable', '');
// load vue components
function getRequiredComponents(){
var requiredComponents = {};
var tagNames = extractNestedComponentsTagNames(elem[0].outerHTML);
for(var index in tagNames){
requiredComponents[tagNames[index]] = components[tagNames[index]];
}
return requiredComponents;
}
// extract from html code the nested vue components to load
function extractNestedComponentsTagNames(html){
let components = html.match(/<sl-.+? /g);
return components.map(function(tag){
return tag.replace('<', '').replace(' ', '')
});
}
return {
pre: function(scope, elem, attrs){
// all the methods to provide to vue
var methods = {};
// all the data to provide to vue
var data = {};
// all the watchers to provide to vue
var watchers = {};
// register attributes from element
for(let key in attrs){
if(!isValidAttribute(key, attrs[key])){
continue;
}
if(key.indexOf('@') === 0){
registerEvent(key, attrs[key]);
}
if(scope[attrs[key]] !== undefined){
// register simple variables
registerInScope(attrs[key]);
}else{
// register variables inside complex expressions
if(typeof attrs[key] == 'string'){
var expressionElements = extractVariablesFromExpression(attrs[key]);
for(let variable in expressionElements){
registerInScope(expressionElements[variable]);
}
}
}
}
// register `{{x}}` from children element
let html = elem[0].innerHTML;
let extractedValues = html.match(/\{\{(.+?)\}\}/g);
for(let value in extractedValues){
registerInScope(extractedValues[value].replace(/\{|\}|\s/g, ''));
}
// register `="x"` from children element
let extractedAttributes = html.match(/=\s*"(.+?)"/g);
for(let value in extractedAttributes){
registerInScope(extractedAttributes[value].replace(/"|=|\s/g, ''));
}
// watch in vue for changes and update angular (for v-model and .sync components)
function buildVueWatchers(data){
for(let key in data){
watchers[key] = {
handler: function(value){
$timeout(function(){
scope[key] = value;
})
},
deep:true
}
}
}
// watch in angular for changes and update vue
function watchAngularData(data){
for(var key in data){
scope.$watch(key, function(value, oldValue){
if(value === oldValue){return;}
data[key] = value;
}, true)
}
}
// check if an attribute contains angular scope variables
function isValidAttribute(attribute, value){
return !(attribute.indexOf('$') == 0 || value === '' || attribute === 'class' || attribute === 'id');
}
// propagates vue methods to angular
function registerEvent(key, value){
methods[value] = function(...args){
scope[value](...args);
}
}
// propagates vue methods to angular
function registerInScope(attribute){
if(typeof scope[attribute] === 'function'){
methods[attribute] = scope[attribute];
}else if(scope[attribute] !== undefined){
data[attribute] = scope[attribute];
scope.$watch(attribute, function(value){
data[attribute] = value;
}, true)
}
}
// extract variables used in angular expressions
function extractVariablesFromExpression(attribute){
return attribute
.split(/;|!|=|>|<|&&|\[|\]|\+|\|\||\(|\)|\.\w+|\s\d+|".*?"|'.*?'|\s/)
.filter((e) => !!e);
}
// init watchers
watchAngularMethod(methods);
watchAngularData(data);
buildVueWatchers(data);
// instasnciate vue application
var vue = new Vue({
el: elem[0],
components: getRequiredComponents(),
methods:methods,
data:data,
watch:watchers,
filters: {
currency,
dotToSpace,
humanize,
size
}
});
},
}
}
}
}
SlVueWrapper.$inject = ['$timeout'];
export default SlVueWrapper;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment