Sub-components in Vue functional components - vue.js

As I was playing around with functional components in Vue, I realized that the components property isn't supported to declare sub-components for functional components. Trying do so will result in an Unknown custom element exception.
Except for using global components, is there any way to use sub-components in functional components?

It is a bit of a workaround, but this Github comment suggest using inject instead to inject the component.
<template functional>
<div>
<component :is="injections.components.SomeChildren"></component>
</div>
</template>
<script>
import SomeChildren from "./SomeChildren.vue";
export default {
inject: {
components: {
default: {
SomeChildren
}
}
}
};
</script>
Not as simple as for regular components, but it does the job.

Related

How to change the language of whole application from the dropdown in Navbar component in vue

I created a language selection dropdown in my Navbar component. So here is my navbar component:
<div>
<h6>{{ translate("welcomeMsg")}} </h6>
<select name="lang" v-model="lang">
<option value="en">English</option>
<option value="de">Deutsch</option>
</select>
</div>
<script>
export default {
mixins: [en, de],
data() {
return {
lang: "en",
};
},
methods: {
translate(prop) {
return this[this.lang][prop];
}
}
}
</script>
So the parent of this component is an Index.vue which is main component in my application.
<div id="app">
<Topnav/>
<Navbar/>
<router-view></router-view>
<Footer/>
</div>
Currently, I am able to change the language in my Navbar component. So according to the selected value in the dropdown in Navbar component, welcomeMsg is changing. What I am trying to do is I want to put this pieve of code to TopBar "{{ translate("welcomeMsg")}} ", and according to the value of the dropdown in Navbar component, I want to change this value.
Can you help me with this or can you give me an idea how to do it?
If I understand you correctly, you want to use translate method inside Topnav component.
This method is however defined in Navbar, so it's not accessible in Topnav.
To use it elsewhere you could create a mixin with this method to import it to any component. I don't recommend this solution though as mixins are making the code messy.
Another solution is to create a component with translate method defined inside. Let this component do just that: translate a message passed by prop and render it inside some div:
<div>
{{ translatedMessage }}
</div>
<script>
mixins: [en, de],
props: {
message: {
type: String,
default: ''
},
language: {
type: String,
default: 'en'
}
},
computed: {
translatedMessage() {
return this[this.language][this.message];
}
}
</script>
You can reuse this component anywhere in the application. You would still need to pass a language prop somehow, possibly the solution would be to use vuex store to do this, since language is probably global for entire application.
For easier and more robust solutions I would use vue-i18n, which #Abregre has already suggested in his comment: https://stackoverflow.com/a/70694821/9463070
If you want a quick solution for a full-scale application and you don't have a problem with dependencies, you could try to use vue-i18n.
It's a plugin for vue that does exactly this for multi-locale websites/apps in Vue.
https://www.npmjs.com/package/vue-i18n
EDIT
Then in order to use it globally in your app, you should use vuex.
Keep the language selection state there and then wherever you want to use it, you make a computed function with the state.language getter.
The translate function should be a global registered filter
https://v2.vuejs.org/v2/guide/filters.html

How to make a Base component that will allow other components to inherit new functionality or code from it in Vue JS

Component 1:-
<template>
<blur :isData="isData">
<!-- logic/implementation of component 1 -->
<div>
</div>
</blur>
<template>
<script>
import blur from "../shared/Blur";
name: "component-1",
components: {
blur,
},
</script>
Just like this component1.vue, I have multiple components which are using blur component. Is it possible that instead of writing and importing blur in every single component, I can make some base class that can transfer the blur functionality in every single component in the folder. Can something like this be achieved in vue ?
With Vue.component you can create globally registered components:
Vue.component('my-component-name', {
// ... options ...
})
Find out more here

How to access and make reactive the :class and :style changes in Vue component

I have a Vue wrapper for Select2 component that I wrote a while ago. The wrapper is able to access the bind:class and bind:style attributes to pass along to the Select2 library, like so:
this.$vnode.data.staticClass // a string value of class="" attribut
this.$vnode.data.class // an Object passed in via bind:class="{}" attribute
this.$vnode.data.style // same as above but for style
this.$vnode.data.staticStyle
Now I would really like to be able to detect changes to these, so that I could pass them along to Select2 innards.
How can this be done?
This is one variant for a functional wrapper around Select2:
<template functional>
<component
:is="injections.components.Select2"
:ref="data.ref"
class="any_desired_static_classes"
:class="[
data.class,
data.staticClass,
]"
:style="[
data.style,
data.staticStyle,
]"
v-bind="data.attrs"
v-on="listeners"
>
<slot />
</component>
</template>
<script>
import Select2 from 'select2';
export default
{
name: 'SelectWrapper',
inject:
{
// workaround since functional components are stateless and can not register components in the normal way
components:
{
default:
{
Select2
}
}
}
};
</script>

pass router params to component object

I have view and I want to load svg based on router params, I have installed vue-loader and it is working if I hard code it.
<template>
<div>
<suit/>
</div>
</template>
<script>
export default {
components:{
suit: ()=>import('../assets/svg/'+Param+'.svg')
}
}
</script>
<style>
</style>
This is what I have. Instead of Param I want to get this.route.params, but when I try this I get undefined which is logical because components wrapper is object. Is there a way to pass a variable here or must I redo the whole thing?
Instead of this.route.params, within a component you should be using this.$route.params. Vue Router Docs.

Sub-class a vue component

I have some repeating functionality that many components will use. So my first thought is I should create a base component and have my components inherit/subclass from this.
But maybe this is not possible in vue.js and maybe its not the vue.js way to do things? If I explain my use-case can you suggest how best I implement this in vue.js?
BasePage.vue
<template>
...
</template>
<script>
export default {
name: 'animated-page',
watch: {
'$route' (to, from) {
// conditionally choose animations depending on route
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
}
</script>
<style>
... my animations
</style>
About.vue
<template>
...
</template>
<script>
export default {
// somehow inherit from BasePage
name: 'about-page',
... 'about' specific code
}
</script>
<style>
</style>
You can use extend to subclass the Vue constructor, and you can use it on components to subclass them.
In general, though, you should prefer composition to inheritance and use mixins where you can.
Mixins are a flexible way to distribute reusable functionalities for Vue components. A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options.