Vue: Unknown custom element after adding component reference within component - vue.js

I'm new to Vue and testing vue-draggable component. Once I add reference to vue-draggable component, I get the error "Unknown custom element: < fxm-form> - did you register the component correctly? For recursive components, make sure to provide the "name" option."
What am I missing here? Similar threads earlier does not have component referenced within a component.
import draggable from "./vue-draggable";
Vue.component('fxm-form', {
name: 'fxm-form',
props: [
"formMode"
],
components: {
draggable
},
data() {
return {
list: ['AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF']
}
},
mounted() {
},
methods:
{
},
template: `
<div>
<h1>Dragable Test</h1>
<draggable :list="list" class="drag-container">
<div v-for="item in list" class="drag-item">{{ item }}</div>
</draggable>
</div>
`
});

When you create a component with Vue.component(), it registers components globally.
As per official docs:
global registration must take place before the root Vue instance is created (with new Vue)
This is because either you've not initialized your component before Vue Instance.
You can register your component inside you Vue Instance.
Here is the working codesandbox example

Related

Vue Test Utils: how to pass Vuelidate validation rules to child components?

while trying to write a component test by using vue test utils, testing interaction between child components and stuff, I am stuck due to usage of Vuelidate from child components. Below is an example simplified:
// parent component code
<template>
<div>
<childA />
</div>
</template>
//childA code
<template>
<input v-model="value" />
</template>
<script>
...
validations: {
value: {
required
}
}
...
</script>
// parent component test
...
const wrapper = mount(MyParentComponent, {
...,
components: {
childA,
},
validations: {
value: required
},
...
})
I have tried to find a solution out there that I could mount (note here that I WANT to mount also the child components, so shallow-mount is not what I look for) the child component, with it's respective Vuelidate validation rules, but I still haven't found any solution.
Instead, my test gives me errors like:
Cannot read property `value` of undefined
which makes sense, since the test cannot access the child component's $v instance.
Has anyone achieved it so far?
For answering your question and after i've did some test i believe you missed the data part inside your mount
mount: render child components
shallowMount: doesn't render child components
MyParentComponent need to have in the options the structure of you're child component so this is why he is returning the error
And i saw that you're passing the import of your component directly but don't forget that your test folder is outside of your src folder
import ChildA from "#/components/ChildA";
will not work instead i propose to use absolute path directly to import your child component or use a configuration to resolve them
const wrapper = mount(MyParentComponent, {
data() {
return {
value: null
}
},
components: {
ChildA: () => import('../../src/components/ChildA'),
},
validations: {
value: required
},
})

Vue 3: Each button click, generate new component and append as child in the target element

I have been searching for a simple solution to generate components in a Vue 3 app programmatically. So far, I've used defineComponent to extend the div component and attach it to the main component via createApp and mount:
Main Component
<template>
<button type="button" v-on:click="addDiv"></button>
<div id="app-main">
</div>
</template>
<script>
import {defineComponent, createApp} from 'vue'
import Div from './components/Div.vue'
export default{
name: 'App',
methods: {
addDiv: () => {
let newDiv = defineComponent({extends: Div});
createApp(newDiv).mount("#app-main");
}
}
}
</script>
Div Component:
<template>
<div>This is a div</div>
</template>
<script>
export default {
name: 'Div'
}
</script>
My issue with this is that mount replaces everything in the target element. If you click the button 3 times, only 1 div appears instead of 3. I need a method where the code appends the component as a child in the target element allowing me to create as many div components as I want. Thanks in advance!
Don't use mount as that is designed to mount a new Vue instance. What you want to use is the <component :is="component_name"> feature and possibly have an array or other data structure containing the list of components you wish to have rendered.
For instance, consider the following simple example:
Vue.component('my-component', {
// component config goes here, omitting for simplicity.
});
new Vue({
el: '#app',
data: {
elements: []
},
methods: {
addNewDiv() {
this.elements.push({type: 'my-component'});
}
}
});
<div id="app">
<component v-for="element in elements" :is="element.type"></component>
</div>
This will iterate over the elements array dynamically and will replace each instance of the <component> tag with whatever is defined by the array element.

Using vue3-tel-input. Why v-model directive doesn't work

I want to use in my app on vuejs 3 this library intl-tel-input.
There is a ready component vue3-tel-input.
Problem: directive v-model doesn't work - variable from data that passed as a model to component doesn't changed.
Every input event, which emitted from component, works three times - visible in the console.
Sanbox
Best regards.
v-model doesn't work in vue3-tel-input because that component hasn't migrated its v-model implementation to Vue 3. The only migration that component seems to have completed is the plugin installation.
In Vue 2, the model property was named "value" and the model-update event was "input". However in Vue 3, they've been renamed to "modelValue" and "update:modelValue", respectively. Notice how vue3-tel-input still uses "value" and "input".
A workaround is for the consumer component to manually bind value and listen to input events, effectively implementing Vue 2 v-model in the parent:
<template>
<vue-tel-input :value="phone" #input="onInput"></vue-tel-input>
<div>{{ phone }}</div>
</template>
<script>
import { VueTelInput } from 'vue3-tel-input'
import 'vue3-tel-input/dist/vue3-tel-input.css'
export default {
components: {
VueTelInput
},
data() {
return {
phone: '+79991234567',
}
},
methods: {
onInput(phone, phoneObject, input) {
if (phoneObject?.formatted) {
this.phone = phoneObject.formatted
}
}
}
}
</script>
demo

How i can to store a set of components for some separate vue apps?

I'm writing own project via Vue.js
I have several applications that include a set of common components and unique components.
I want to be able to configure configs for these applications, in which I could specify the necessary components and data for them, like this:
"load": [
"Core.vue",
"Accordion.vue",
"Buttons.vue",
"Table.vue",
"etc"
],
"components": {
"table": {
"name": "title"
},
"accordion": {
"tabs": {
"tab1": {
"name": 'tab1'
},
"tab2": {
"name": 'tab2'
}
}
}
}
and then components take this data and show it:
<template>
<div> {{ name }} </div>
</template>
<script>
export default {
name: "Table",
props: ['name']
}
</script>
I think it can be JSON or .yml file (or any other format), but i don't understand how to process them?
i think you are looking for dynamic component .
that way, you can keep the components you need in some json file, import it to the parent component and dynamically create the children, for example, if you have a jsonMap.json file:
{
landingPage:{
banners:['child1','child2']
}
}
then landingPage.vue:
<template>
<div>
<component v-for="(banner,index) in getBanners" :is="banner" :key="index"</component>
//this is a dynamic component. the 'is' attribute determine what it will be.
</div>
</template>
<script>
import jsonMap from 'path/to/jsonMap'
import child1 from 'path/to/shared/components/child1.vue'
import child2 from 'path/to/shared/components/child2.vue'
import child3 from 'path/to/shared/components/child3.vue'
export default {
name: 'landingPage',
components:{child1,child2,child3},
methods:{
getBanners(){
return jsonMap[this.name].banners
}
}
}
</script>
landing page will render 2 banners- according to the jsonMap.
note that despite you must import and register your components, that can not be done dynamically.
I would make a component called ModuleLoader.vue in that component import all of your components in the load array. Then make a dynamic component that takes a computed property or just a prop to load the component.
App.vue
<ModuleLoader
v-for="(module, index) in modules"
:key="`ModuleLoader_${index}`"
v-bind="module"
/>
ModuleLoader.vue
<component
:is="moduleData.type"
v-bind="moduleData.content"
/>
You'll need a ModuleData prop and you'll have to import the components you're loading into this file
Check out this fiddle

How to pass props to a vue component at initialization inside single file vue components (dependency injection in vue-loader)?

I'm building a TabbedDetailView reusable component in vue. The idea is that the tab-detail component receives a list of objects which have a title and a component. It then does the logic so that when you click on a tab, then the component is displayed. The problem is that this components have a prop that is a user_id. How do I insert this prop into the components from outside of the template (directly in the script)?
For example (using single file vue components with webpack):
TabDetail.vue
<template>
<div>
<nav class="tabs-nav">
<ul class="tabs-list">
<li class="tabs-item" v-for='tab in tabs'>
<a v-bind:class="{active: tab.isActive, disabled: !tab.enabled}" #click="switchTab(tab)">{{tab.title}}</a>
</li>
</ul>
</nav>
<div v-for='tab in tabs'>
<component :is="tab.detail" v-if='tab.isActive'></component>
</div>
</div>
</template>
<script>
export default {
name: 'NavigationTabs',
props: ['tabs'],
created: function() {
this.clearActive();
this.$set(this.tabs[0], 'isActive', true);
},
methods: {
clearActive: function() {
for (let tab of this.tabs) {
this.$set(tab, 'isActive', false);
}
}, switchTab: function(tab) {
if (tab.enabled) {
this.clearActive();
tab.isActive = true;
}
},
},
};
</script>
The idea is that this can be reused by only passing a props object with titles and components. eg. tabs = [{title: 'Example1', component: Component1}{title: 'Example2', component: Component2}] I want to be able to instantiate this components with props before passing them. eg. tabs = [{title: 'Example1', component: Component1({user_id: 5})}{title: 'Example2({user_id: 10})', component: Component2}]).
SomeComponent.vue
import Vue from 'vue';
import TabDetail from '#/components/TabDetail'
import Component1 from '#/components/Component1';
const Componenet1Constructor = Vue.extend(Component1);
export default {
data() {
return {
tabs: [
{title: 'Componenent 1', detail: new Component1Constructor({propsData: {user_id: this.user_id}})}
{title: 'Component 2', detail: Component2},
{title: 'Component 3', detail: Component3},
],
};
}, props: ['user_id'],
components: {'tab-detail': TabbedDetail},
}
<template>
<div>
<tab-detail :tabs='tabs'></tab-detail>
</div>
</template>
Component1.vue
export default {
props: ['user_id'],
};
<template>
<div>
{{ user_id }}
</div>
</template>
The approach above raises de error:
[Vue warn]: Failed to mount component: template or render function not defined.
I think this is a good idea because I'm trying to follow the dependency injection design pattern with components. Is there a better approach to this problem without using global state?
This is could be done via Inject Loader when using vue loader with single file vue components but it adds a lot of unnecessary complexity and it's mostly meant for testing. It seems like the preferred way of managing state is by using a global state management store like Vuex.