Using Vue Components in Angular - javascript

I have a project that is built in Vue and I want to reuse the components from the Vue application in an Angular application so I don't have to go and rebuild every single component from scratch.
I saw this tutorial on medium: How to use Vue 2.0 components in an angular application, but that tutorial is for AngularJS.
I'm wondering if anyone has done this before, if it's worth it and if anyone knows of any tutorials or reference material.

Wrap your Vue components as native Web Components.
Since Angular supports using custom Web Components, you'll be able to use the Vue components (wrapped as Web Components).
To Angular it doesn't make a difference if the custom Web Components were generated by Vue or not (for all Angular knows, they could be native HTML elements).
Demo
Runnable DEMO here.
The demo is an Angular 5 app. The Vue custom component is defined in index.html. Notice how in app/app.component.html it is used directly in the template, as if it were a native element.
Step by step below.
In Vue
Use vue-custom-element to wrap your Vue components as Web Components:
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-custom-element#3.0.0/dist/vue-custom-element.js"></script>
<script>
const MyVueWebComp = {
props: ['msg'],
template:`
<div style="border: 3px dashed green; padding: 5px">
I am my-vue-web-comp.<br>
Value of "msg" prop: {{ msg }}<br>
<input v-model="text"><button #click="addText">Click me</button>
<div v-for="t in texts">
Text: {{ t }}
</div>
</div>
`,
data() {
return {
text: '',
texts: []
};
},
methods: {
addText() {
this.texts.push(this.text);
this.text = '';
}
}
};
Vue.customElement('my-vue-web-comp', MyVueWebComp);
</script>
That will create a <my-vue-web-comp> web component that can be used directly in the DOM, without the need to have a working Vue instance.
The above is just a demo runnable directly in the browser. If you have .vue files and a vue-cli app, you'll need to do npm install vue-custom-element --save and then create a .js file like:
import Vue from 'vue';
import vueCustomElement from 'vue-custom-element';
import MyElement from './MyElement.vue';
Vue.use(vueCustomElement);
Vue.customElement('my-element', MyElement);
And then this, when bundled, will generate a .js file that can be imported directly as a single <script> tag, instead of the whole code and script tags above.
For more details, check vue-custom-element's docs.
In Angular
Now, in the Angular app, after importing the Web Components (being them Vue-generated or not), configure them to be used by Angular by adding schemas: [CUSTOM_ELEMENTS_SCHEMA] in your #NgModule:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
//...
#NgModule({
// ...
schemas: [
CUSTOM_ELEMENTS_SCHEMA // added this
]
})
export class AppModule {
Now use the Web Components (generated from Vue or not) directly in Angular templates. E.g. the component defined in the code above could be used like:
<my-vue-web-comp [msg]="name"></my-vue-web-comp>
In fact, the runnable demo shows an example of that usage.
Limitations
You may need polyfills for older browser support. Please check vue-custom-element's docs for more details.

Related

How can I access/mutate Vue component properties from vanilla JS

I have a Vue 2 project made with Vue CLI, and I plan to distribute it as a library, ideally with the dependencies and Vue syntax stuff abstracted away by some kind of wrapper script. I would like to allow this kind of interaction:
// mount the component on a plain JS webpage
const myComponent = new MyComponent('#my-component');
// handle events from the component in vanilla JS
myComponent.on('load', someHandler);
// (A.) call a component method and get a return value
const processedData = myComponent.process(123);
// (B.) access/mutate reactive component data properties
myComponent.setMessage('Hello world!');
I have tried changing the "build target" to build a Libary or a Web Component as mentioned in the Vue documentation. I can mount the library component just fine, and handle events, but it doesn't mention how I might interact with the component data from outside the Vue VM (see comments A and B).
How can I access Vue component methods and data properties from outside the Vue VM, in vanilla JS?
To access the Vue component properties (and methods) outside of the VM, you can mount it with a "template ref" like this:
const vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount('#mount-element");
and then you can call its methods like this:
vm.$refs.myComponent.someFunction();
You'll get the returned values and it will access/mutate reactive properties inside the VM as expected.
To use the class syntax described in the original question, we can create a simple class to wrap the vue component:
// import the component built by Vue CLI with the "library" build target
// (puts `MyComponent` in the global namespace)
import './MyComponent.umd.min.js';
import Vue from 'https://unpkg.com/vue#2/dist/vue.esm.browser.min.js';
export default class {
constructor(mountElement) {
// mount vue VM with a template ref to access its properties
const thisClass = this;
this.vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount(mountElement);
this.component = this.vm.$refs.myComponent;
}
// define methods that could call this.component's functions
someFunction() {
// do stuff
return this.component.someFunction()
}
}
It seems to work pretty well. A possible improvement would be to build the component library with a different tool, since Vue CLI v3 (with Vue v2 projects) can't output ESM module files, so the best we can do is a UMD modle that gets defined globally.

How to convert component string as real component in vue

Hi i have a requirement for plugin store where i need to load component from core into plugins and inside plugin i need to convert that string into real component so that i can use it.
Note: better approach is most welcome in the view of plugin store inside core
my pseudo logic
get component from specified path through http
convert loaded string into real vue component and store it in a variable
render it in dom
let componentAsString =
`<template>
<div>
<h class="red">{{title}}</h>
<!--<A></A> -->
</div>
</template>
<script>
//import A from './components/A'
export default {
name: 'App',
data(){
return {
title:'Hello World'
}
},
/*components: {
A
}*/
}
</script>
<style lang="scss" scoped>
.red{color:red;}
</style>`;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
You have chosen very difficult approach to solve the requirement. Main problem is the content of the string - it is essentially a Vue SFC (.vue file). In order to turn this SFC into a Vue component usable in the browser a lot must be done. You must use Webpack (or Rollup or any other bundler), use vue-loader inside to parse the SFC and use different Webpack loaders to process each section of SFC (Babel to transpile the <scipt> block, sass-loader and sass compiler to turn <style lang="scss"> into CSS)
There are tools for doing most of this (with limitations) in the browser like for example vue3-sfc-loader but the cost is huge - vue3-sfc-loader weights around 1.4MB of minified javascript (add Vue itself or potential CSS preprocessor on top of that) and I bet performance of such solution will not be great either
Much easier approach is to use this standard tooling at build time
Just create your components as you normally do - in .vue files
Use your components as Async Components and build a "dictionary" of available components. Those components will be build at build time into separate js files and loaded into a browser on demand (when used)
// ComponentStore.js
export default {
component1: () => import('./components/component1'),
component2: () => import('./components/component2'),
}
Note: process of creating this dictionary can be automated too (inspiration)
Use the component as dynamic component
// DynamicComponent.vue
<template>
<component :is="comp" />
</template>
<script>
import ComponentStore from "ComponentStore.js"
export default {
props: ['componentName'],
computed: {
comp() {
return ComponentStore[this.componentName]
}
}
}
</script>

Nuxtjs plugin registration

I am curious as to the methodology Nuxt.js uses to register a plugin. I have been reading the documentation for Nuxt.js and I am slightly confused as to the registration methodology.
I do not want to register plugins such as vue-flag-icon globally.
My understanding is when we register the plugin we use the plugin folder as such:
import Vue from 'vue'
import FlagIcon from 'vue-flag-icon'
Vue.use(FlagIcon)
I can now use the flag component anywhere in my app - I dont want this!!!
I want to be able to load plugins into the components that need them ONLY.
I have tried loading them as a component like:
components:{
'flag': ()=> import('path to plugin') // #/plugins/vue-flag-icon
}
This does not work.
I changed my plugins script to:
import Vue from 'vue'
import FlagIcon from 'vue-flag-icon'
export default () => {
Vue.use(FlagIcon)
}
And then tried to register the plugin within the components like so:
import flag from '#/plugins/vue-flag-icon';
created(){
flag()
}
My questions really are:
How can I register a plugin within the component without importing the vue instance? (I think is called a bus)
Is it bad practice to import the plugins directly into components?
Is registering all the plugins within nuxt.config.js creating a larger download file for users to download (example: registering vue-twix is not necessary on pages that do not have textareas) or will nuxt/webpack handle the removal of unnecessary plugins on a page by page or component by component basis (so I don't have to even worry about this)?. If this is the case than I dig nuxt.
Thanks
If you want to use FlagIcon only on certain components, you have to import it and use it on every component you want to use.
<template>
<flag iso="ca" />
</template>
<script>
import FlagIcon from 'vue-flag-icon'
export default () => {
components: {
FlagIcon
}
</script>
This should work.
For other components, remember to import any style sheets the component might require:
<style lang="scss" scoped>
#import 'path/to/plugin/style.css';
</style>

Importing javascript file for use within vue component

I am working on a project that requires using a js plugin. Now that we're using vue and we have a component to handle the plugin based logic, I need to import the js plugin file within the vue component in order to initialize the plugin.
Previously, this was handled within the markup as follows:
<script src="//api.myplugincom/widget/mykey.js
"></script>
This is what I tried, but I am getting a compile time error:
MyComponent.vue
import Vue from 'vue';
import * from '//api.myplugincom/widget/mykey.js';
export default {
data: {
My question is, what is the proper way to import this javascript file so I can use it within my vue component?
...
Include an external JavaScript file
Try including your (external) JavaScript into the mounted hook of your Vue component.
<script>
export default {
mounted() {
const plugin = document.createElement("script");
plugin.setAttribute(
"src",
"//api.myplugincom/widget/mykey.js"
);
plugin.async = true;
document.head.appendChild(plugin);
}
};
</script>
Reference: How to include a tag on a Vue component
Import a local JavaScript file
In the case that you would like to import a local JavaScript in your Vue component, you can import it this way:
MyComponent.vue
<script>
import * as mykey from '../assets/js/mykey.js'
export default {
data() {
return {
message: `Hello ${mykey.MY_CONST}!` // Hello Vue.js!
}
}
}
</script>
Suppose your project structure looks like:
src
- assets
- js
- mykey.js
- components
MyComponent.vue
And you can export variables or functions in mykey.js:
export let myVariable = {};
export const MY_CONST = 'Vue.js';
export function myFoo(a, b) {
return a + b;
}
Note: checked with Vue.js version 2.6.10
try to download this script
import * from '{path}/mykey.js'.
or import script
<script src="//api.myplugincom/widget/mykey.js"></script>
in <head>, use global variable in your component.
For scripts you bring in the browser way (i.e., with tags), they generally make some variable available globally.
For these, you don't have to import anything. They'll just be available.
If you are using something like Webstorm (or any of the related JetBrains IDEs), you can add /* global globalValueHere */ to let it know that "hey, this isn't defined in my file, but it exists." It isn't required, but it'll make the "undefined" squiggly lines go away.
For example:
/* global Vue */
is what I use when I am pulling Vue down from a CDN (instead of using it directly).
Beyond that, you just use it as you normally would.
I wanted to embed a script on my component and tried everything mentioned above, but the script contains document.write. Then I found a short article on Medium about using postscribe which was an easy fix and resolved the matter.
npm i postscribe --save
Then I was able to go from there. I disabled the useless escape from eslint and used #gist as the template's single root element id:
import postscribe from 'postscribe';
export default {
name: "MyTemplate",
mounted: function() {
postscribe(
"#gist",
/* eslint-disable-next-line */
`<script src='...'><\/script>`
);
},
The article is here for reference:
https://medium.com/#gaute.meek/how-to-add-a-script-tag-in-a-vue-component-34f57b2fe9bd
For anyone including an external JS file and having trouble accessing the jQuery prototype method(s) inside of the loaded script.
Sample projects I saw in vanilla JS, React and Angular were simply using:
$("#someId").somePlugin(options)
or
window.$("#someId").somePlugin(options)
But when I try either of those in my VueJS component I receive:
Error: _webpack_provided_window_dot$(...).somePluginis not a function
I examined the window object after the resources had loaded I was able to find the jQuery prototype method in the window.self read-only property that returns the window itself:
window.self.$("#someId").somePlugin(options)
Many examples show how to load the external JS file in VueJS but not actually using the jQuery prototype methods within the component.

How to import vuejs component dependencies in a MPA?

In a single page app, I can do this to include a component inside a component.
$ npm install sagalbot/vue-select
<template>
<div id="myApp">
<v-select :value.sync="selected" :options="options"></v-select>
</div>
</template>
<script>
import vSelect from "vue-select"
export default {
components: {vSelect},
data() {
return {
selected: null,
options: ['foo','bar','baz']
}
}
}
</script>
How can I do this in a MPA, where I have bunch of js files or sometimes inline javascript in different pages?
I am not using any build system.
I would highly recommend using vue-cli, but if for some reason that is not possible, then I believe,though I have never tried it, you would basically need to add all your components and code into a single long js file, or include them in the right order in your html documents.
Certain component libraries can work this way, like vuetify. You simply include the whole vuetify.js file after vue.js and then you can use all the components available.
I think it would be quite a lot of work to do anything of much size, but if it is something really small you could add components in the following manner, one after another.
var componentTemplate =
`
// Template code..
`;
Vue.component('my-cool-component', {
template: componentTemplate,
data: function() {
return {
//
}
}
});

Categories

Resources