Vue 3: How to access other plugins in a plugin? - vue.js

In a component's <script setup> block, I can access plugins with inject().
But how can I access other plugins' provide() in a later-use()d plugin?
I hoped there would be app.inject() in the app object that passed to the plugin's install() function, but there is no such function.
Maybe I can use _context property of the app project, but it seems like a hack.

Related

Exposing functions on a Vue-defined Web Component (Custom Element)

Per the Vue docs, it's possible to build components in Vue (v3) and package them as native Web Components for use with any framework or none at all.
As I've already found, the gap between design models for Vue components and Web Components can make this complex and sometimes a straight-up bad idea (at what point is it better and more maintainable to just go ahead building fully-native components?)... But let's assume for a moment that it's necessary here.
My question - What's the best way to expose a function-like interface on a Vue-built Web Component (to parent nodes)?
The Vue doc discusses passing in reactive data via props/slots, and publishing CustomEvents from the components, but I don't see mention of
taking function calls (or at a stretch, events) from outside. As far as I can tell this is a pretty strong assumption that data and event flow on the rest of the app/page works in a very "Vue-like way"?
For now, my workaround on this is to look up the host element in onMounted() (as per this question) and just set whatever extra properties are required at that point (hoping they shouldn't be required before the Vue component mounts, because I'm not aware of any external events raised when Vue finishes mounting the custom element).
This way the function can still be defined in the context of, and access variables/etc from, the setup function - but can be called by other elements on the page that only have a reference to the element, not the Vue component.
Can't say I like it much though:
<template>
<div ref="someElInTemplate">...</div>
</template>
<script lang="ts">
interface MyCoolHTMLElement extends HTMLElement {
myCoolFunction: () => void;
}
</script>
<script setup lang="ts">
const someElInTemplate = ref<HTMLElement>();
function myCoolFunction() { }
onMounted(() => {
const hostNode = (
somElInTemplate.value?.getRootNode() as ShadowRoot | undefined
)?.host as MyCoolHTMLElement;
hostNode.myCoolFunction = myCoolFunction;
});
</script>

Vue 3: Composition API with external templates

So, I come from Angular world where by default we are separating view from business logic... and I don't like single file components, especially when they are getting bigger and convoluted.
Before in vue 2, I would have myComponent.vue with script tag and have it like so:
<template src="./myComponent.html"></template>
To call the view file... and it would work... all the methods were accessible
but now, if I use the setup way
<script setup lang="ts">
const suggets = () => {
}
</script>
<template src="./myComponent.html"></template>
I would get a typescript error that suggest was declared and never used...
Also for example:
<carbon-search /> component would not work
Failed to resolve component: carbon-search If this is a native custom
element, make sure to exclude it from component resolution via
compilerOptions.isCustomElement.
Any good and elegant way to split the component view and business logic (and css)?

Inject some logic into destroyed

I'm writing a module which need to run some code when every vue components destroyed. Is there any mechanism to inject my code into to component destroyed hook?
As for VUE 2 - You can use a mixin as described here https://v2.vuejs.org/v2/guide/mixins.html
A mixin can contain a beforeDestroy or destroyed hook.
Hook functions with the same name are merged into an array so that all of them will be called. Mixin hooks will be called before the component’s own hooks.
You can also use a global mixin to address all components.
You can also apply a mixin globally. Use with caution! Once you apply a mixin globally, it will affect every Vue instance created afterwards.
Vue.mixin({
destroyed() {
console.log('Component has been destroyed logic');
}
})

Unknown html tag warning of Bootstrap-Vue.js support in WebStorm

I'm using WebStorm 2017.2.4 and webpack Vue.js project. I have added bootstrap-vue.js to my project and would like to see hints for it and components support.
But instead of that I have got "Unknown html tag" warning.
BTW: bootstrap-vue works as expected when running project.
Do you have any suggestions how to make it work?
UPDATED on 2019/07/30
PHPShtorm(WebStorm) was updated to 2019.2 and now they added better support for vuejs libraries:
https://blog.jetbrains.com/webstorm/2019/07/webstorm-2019-2/#development_with_vue
I've just tested and it works.
OLD answer
I solved this issue by adding components manually.
According to: https://bootstrap-vue.js.org/docs/#individual-components-and-directives
I created new file, e.g. bootstrap.js then register globally components which required
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue/dist/bootstrap-vue.css';
import Vue from 'vue';
import navbar from 'bootstrap-vue/es/components/navbar/navbar';
import container from 'bootstrap-vue/es/components/layout/container';
// ...
Vue.component('b-navbar', navbar);
Vue.component('b-container', container);
// ...
It work for me in phpstorm 2018.1
Bootstrap vue uses very dynamic way of defining components. I am using PyCharm with vuejs extension which is unable to resolve the components when registered using
import { Layout } from 'bootstrap-vue/es/components'
Vue.use(Layout)
What I use to do is make a new file bootstrap.js in components directory, and register all bootstrap components I would use like
import Vue from 'vue'
import bContainer from 'bootstrap-vue/es/components/layout/container'
import bRow from 'bootstrap-vue/es/components/layout/row'
import bCol from 'bootstrap-vue/es/components/layout/col'
Vue.component('b-container', bContainer);
Vue.component('b-col', bCol);
Vue.component('b-row', bRow);
and then import this file in main.js
import './components/bootstrap'
Just a little cleaner solution.
#Updated: There're two ways to fix "Unknown html tag" warning: (Global and Local Registration)
Global Registration :
You should have to register your component globally Vue.component(tagName, options) before creating the new Vue instance. For example:
Vue.component('my-component', {
// options
})
Once registered, a component can be used in an instance’s template as a custom element, <my-component></my-component>. Make sure the component is registered before you instantiate the root Vue instance. Here’s the full example:
HTML:
<div id="example">
<my-component></my-component>
</div>
JS:
// global register
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// create a root instance
new Vue({
el: '#example'
})
Which will render HTML::
<div id="example">
<div>A custom component!</div>
</div>
Local Registration :
You don’t have to register every component globally. You can make a component available only in the scope of another instance/component by registering it with the components instance option:
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> will only be available in parent's template
'my-component': Child
}
})
The same encapsulation applies for other registerable Vue features, such as directives.
Read more at https://v2.vuejs.org/v2/guide/components.html#Using-Components
#Before Updated:
In WebStorm, a library is a file or a set of files whose functions and methods are added to WebStorm's internal knowledge in addition to the functions and methods that WebStorm retrieves from the project code that you edit. In the scope of a project, its libraries by default are write-protected.
WebStorm uses libraries only to enhance coding assistance (that is, code completion, syntax highlighting, navigation, and documentation lookup). Please note that a library is not a way to manage your project dependencies.
Source: https://www.jetbrains.com/help/webstorm/configuring-javascript-libraries.html
Simply, upgrade WebStorm from version 2017.2.4 to 2017.3 which fixed this issue. It is tested.

Vue instance inside another Vue instance

I’m integrating Vue with a CMS called AEM thats works basically as component base system like Vue works too. But instead of having a webpack and imports of .vue files, every component on this CMS is a new Vue instance (new Vue({…})). So on my page I have a lot of Veu instances that communicate with each other using the same store (vuex).
This is actually working fine, but I have a scenario when I need a CMS component inside another. Since both this components are a unique vue instance and the “el” property from the parent includes the “el” from the child, the child component doesn’t work.
I know that this is not the expected use of this lib, but is there any way that I can tell or share the same “context” on both vue instances or even another approach for this scenario.
Thx,
Alexandre.
There should be only one instance of Vue.
I suggest you to create single empty Vue instance inside the body tag
All your existent Vue instances transform into components
Register all components in the root Vue instance
With this approach it will be fine to nest one component into another
You should use only one Vue instance as #shrpne mentioned.
If you keep instantiating Vue instances for every component, you'll run into issues while debugging or with component communication and overall this becomes very missy and you miss out on parent-child communication and inheritance provided by Vue.
I don't know about your Vue architecture, but I am currently working on a manual for working with Vue in AEM.
The basic premise is to use Vue's inline-template and vanilla-js, No typescript, nodeJS build, jsx or anything else at build time, just vanilla-js so that when your page is loaded and even before your js bundle is present, the DOM is already there, you just need to mount components by instantiating one Vue instance that will mount all components. This is also great for SEO (unless you plan to server-side render Vue components in java... which is possible theoretically, but good luck!)
Here is a sample AEM/Vue component:
<simple-counter inline-template>
<button v-bind:style="style" v-on:click="add">${properties.clicksText || 'clicks: '} {{ counter }}</button>
</simple-counter>
the JS:
notice how it does not have a template in the JS, because it's inlined above
Vue.component('simple-counter', {
data: function() {
return {
counter: 0,
style: {
color: 'red',
width: '200px'
}
}
},
methods: {
add: function() {
this.counter = this.counter + 1;
this.style.color = this.style.color == 'red' ? 'green' : 'red';
}
}
})
You can build more AEM components in this fashion, then at the end of your clientlib when all your Vue components have been registered, you can run:
new Vue({ el: '#app'})
This, off course, assumes that your page body or some other parent element has the id: app.
The second part of this, how do you enable re-mount of components after authoring dialog is submitted, you could just refresh the page.
I have a question about how we can re-mount components without refreshing the page here
The basic idea is to add an afteredit event to the component and run a new Vue instance only on the newly mutated component... still working on that
Solution:
Replace all new Vue(...) stuff into Vue.component(...) Vue.extend(...) etc for better interface management.
Only use ONE Vue instance witch is new Vue({...options})
Slice your vuex store into modules.
Google teacher knows everything.