I'm trying to write a story for a component that references this.$route.params. I'm not sure how to synthetically define this.$route in the context of a story. I think the solution is to use decorators, but all the examples in the docs focus on rendering, like adding a wrapping <div> etc. I'm not sure how to inject values.
I also found this project which appears designed for this exact situation, but it hasn't been maintained in years and README references outdated syntax that doesn't match modern versions of Storybook, so I don't think it's an option.
Here's what doesn't work:
import AssetShow from '../app/javascript/src/site/components/assets/Show'
export default {
title: 'Site/AssetShow',
component: AssetShow,
parameters: {
}
};
export const CustomerSpotlight = () => ({
components: { AssetShow },
template: '<AssetShow />',
});
import Vue from 'vue'
import VueRouter from 'vue-router'
import StoryRouter from 'storybook-vue-router';
CustomerSpotlight.decorators = [
(story) => {
Vue.use(VueRouter)
return {
components: { story },
template: '<story />'
}
}
];
The component I'm writing the story for has this:
mounted() {
axios.get(`.../bla/${this.$route.params.id}.json`)
},
...which causes Storybook to throw this error: TypeError: Cannot read properties of undefined (reading 'params')
I suppose that your intention is to do something with the story's component based on the route parameters?
If that is the case, then I don't think you need to define the route.params within the story context. I suggest either keeping that code within the component itself, or create an option within the story for the user to simulate adding parameters to the path. Which you can simply have as an input text / select field that you send down to the component as a prop.
In my nuxt component I want to use the ace editor:
import Ace from "ace-builds/src-noconflict/ace"
when the component is mounted I am doing the following:
this.editor = Ace.edit...
Obviously the window is not defined on the server on page reload. But unfortunately I just can't find a solution to fix this issue.
Is there a way to import a package on the mounted() hook?
I already tried
const Ace = require("ace-builds/src-noconflict/ace")
But that doesn't quite seem to work. Do you have any ideas to solve this issue?
I already tried to register a plugin plugins/ace.js:
import Vue from "vue"
import Ace from "ace-builds/src-noconflict/ace"
Vue.use(Ace)
registered it in nuxt.config.js:
plugins: [
{ src: "~/plugins/ace", mode: "client" }
],
But how do I use Ace in my component now? It is still undefined...
Since the error was thrown during the import statement, I'd recommended using dynamic imports as explained in my other answer here.
async mounted() {
if (process.client) {
const Ace = await import('ace-builds/src-noconflict/ace')
Ace.edit...
}
},
From the official documentation: https://nuxtjs.org/docs/2.x/internals-glossary/context
EDIT: I'm not sure about Ace and it's maybe a drastic change but you may also give a look to vue-monaco which is elbow-to-elbow popularity wise (vanilla Monaco editor).
EDIT2: mounted actually only runs on the client so you could strip the process.client conditional. Meanwhile, I do let it here in case you want to run some logic in other hooks like created (which are run on both server + client). More info here.
EDIT3: not directly related to the question, but some packages expose a component which is only available on the client-side (no SSR support), in those cases you could import the component only on the client side and easily prevent any other errors.
Nuxt Plugin
IMHO you were on the right track with the "plugin" solution. Only mistake was the
Vue.use(Ace) part. This only works for vue plugins.
The plugin file could look somewhat like that:
import Ace from 'ace-builds/src-noconflict/ace'
import Theme from 'ace-builds/src-noconflict/theme-monokai'
export default ({ app }, inject) => {
inject('ace', {
editor: Ace,
theme: Theme
})
}
Then you could use this plugin and initiate the editor in a component this way:
<template>
<div id="editor">
function foo(items) {
var x = "All this is syntax highlighted";
return x;
}
</div>
</template>
<script>
export default {
data () {
return {
editor: {}
}
},
mounted () {
this.editor = this.$ace.editor.edit('editor')
this.editor.setTheme(this.$ace.theme)
}
}
</script>
I just want to be able to call
{{ globalThing(0) }}
in templates, without needing to define globalThing in each .vue file.
I've tried all manner of plugin configurations (or mixins? not sure if Nuxt uses that terminology.), all to no avail. It seems no matter what I do, globalThing and this.globalThing remain undefined.
In some cases, I can even debug in Chrome and see this this.globalThing is indeed defined... but the code crashes anyway, which I find very hard to explain.
Here is one of my many attempts, this time using a plugin:
nuxt.config.js:
plugins: [
{
src: '~/plugins/global.js',
mode: 'client'
},
],
global.js:
import Vue from 'vue';
Vue.prototype.globalFunction = arg => {
console.log('arg', arg);
return arg;
};
and in the template in the .vue file:
<div>gloabal test {{globalFunction('toto')}}</div>
and... the result:
TypeError
_vm.globalFunction is not a function
Here's a different idea, using Vuex store.
store/index.js:
export const actions = {
globalThing(p) {
return p + ' test';
}
};
.vue file template:
test result: {{test('fafa')}}
.vue file script:
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions({
test: 'globalThing'
}),
}
};
aaaaaaaaand the result is.........
test result: [object Promise]
OK, so at least the method exists this time. I would much prefer not to be forced to do this "import mapActions" dance etc. in each component... but if that's really the only way, whatever.
However, all I get is a Promise, since this call is async. When it completes, the promise does indeed contain the returned value, but that is of no use here, since I need it to be returned from the method.
EDIT
On the client, "this" is undefined, except that..... it isn't! That is to say,
console.log('this', this);
says "undefined", but Chrome's debugger claims that, right after this console log, "this" is exactly what it is supposed to be (the component instance), and so is this.$store!
I'm adding a screenshot here as proof, since I don't even believe my own eyes.
https://nuxtjs.org/guide/plugins/
Nuxt explain this in Inject in $root & context section.
you must inject your global methods to Vue instance and context.
for example we have a hello.js file.
in plugins/hello.js:
export default (context, inject) => {
const hello = (msg) => console.log(`Hello ${msg}!`)
// Inject $hello(msg) in Vue, context and store.
inject('hello', hello)
// For Nuxt <= 2.12, also add 👇
context.$hello = hello
}
and then add this file in nuxt.config.js:
export default {
plugins: ['~/plugins/hello.js']
}
Use Nuxt's inject to get the method available everywhere
export default ({ app }, inject) => {
inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}
Make sure you access that function as $myInjectedFunction (note $)
Make sure you added it in nuxt.config.js plugins section
If all else fails, wrap the function in an object and inject object so you'd have something like $myWrapper.myFunction() in your templates - we use objects injected from plugins all over the place and it works (e.g. in v-if in template, so pretty sure it would work from {{ }} too).
for example, our analytics.js plugin looks more less:
import Vue from 'vue';
const analytics = {
setAnalyticsUsersData(store) {...}
...
}
//this is to help Webstorm with autocomplete
Vue.prototype.$analytics = analytics;
export default ({app}, inject) => {
inject('analytics', analytics);
}
Which is then called as $analytics.setAnalyticsUsersData(...)
P.S. Just noticed something. You have your plugin in client mode. If you're running in universal, you have to make sure that this plugin (and the function) is not used anywhere during SSR. If it's in template, it's likely it actually is used during SSR and thus is undefined. Change your plugin to run in both modes as well.
This would be the approach with Vuex and Nuxt:
// store/index.js
export const state = () => ({
globalThing: ''
})
export const mutations = {
setGlobalThing (state, value) {
state.globalThing = value
}
}
// .vue file script
export default {
created() {
this.$store.commit('setGlobalThing', 'hello')
},
};
// .vue file template
{{ this.$store.state.globalThing }}
I am looking to setup emojis to my chat-app project.
I really liked the emojis in slack / tweeter and I would like to have something similar.
I found the following libraries: (if anyone can suggest a better library I would love to hear)
emojionearea
wdt-emoji-bundle
I am not sure how to load these libraries and use them in a VueJS app.
Could anyone please assist with how to load and use them?
I would like to mention I tried to use emoji-mart-vue
but not sure how to add the the component to the template at run time.
Thanks a lot
tldr; Checkout demo and code on JSFiddle
This answer is divided into two parts. First part deals with how to import all the stuff into the environment while the second part deals with how to use it Vue.
This solution uses EmojiOne as the emoji provider and EmojioneArea to provide emoji autocomplete behavior.
Part 1: Adding the libraries
There are three ways in which you can do this.
Using Global <script> tag (not recommended for anything serious): You can add the <script> tags for all the dependencies in order and simply start using the Global objects.
Add these <script> in order:
https://code.jquery.com/jquery-3.3.1.min.js
https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.min.js
https://cdn.jsdelivr.net/npm/emojione#3.1.6/lib/js/emojione.min.js
https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.4/jquery.textcomplete.min.js
https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea.min.js
Using AMD modules (using require.js): You can use the following configuration to load these modules asynchronously using require.js
require.config({
paths: {
'jquery': 'https://code.jquery.com/jquery-3.3.1',
'Vue': 'https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue',
'emojione': 'https://cdn.jsdelivr.net/npm/emojione#3.1.6/lib/js/emojione',
'jquery.textcomplete': 'https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.4/jquery.textcomplete',
'emojionearea': 'https://cdnjs.cloudflare.com/ajax/libs/emojionearea/3.4.1/emojionearea'
},
shim: {
'emojione': {
'exports': 'emojione'
}
}
})
You can then require these modules in your entry point as
require(['jquery', 'Vue', 'emojione'], function($, Vue, emojione){ /* ... */ })
Using webpack: All the above libraries are available via npm and you install and use them just as you would do with any other library. No special config required.
BONUS: Using webpack with require.js and offloading network and caching loads to CDN!
I often use this setup for my projects and this improves reliability and performance of your site. You can use webpack.externals to instruct webpack to not bundle any vendor dependencies and instead you provide them yourself, either by manually adding <script> tags or using require.js.
Start by adding this to your webpack.<whatever>.js
//...
output: {
libraryTarget: 'umd' // export app as a library
},
//...
externals: {
'jquery': 'jquery',
'vue': 'Vue',
'emojione': 'emojione'
},
//...
Then, in your require.js entry, add this
//...
map: {
// any module requesting jquery should get shield
"*": {
"jquery": "jquery-shield"
},
// shield should get original jquery
"jquery-shield": {
"jquery": "jquery"
},
// patch plugins
"jquery.textcomplete": {
"jquery": "jquery"
},
"emojionearea": {
"jquery": "jquery"
}
}
//...
// define shield, require all the plugins here
define('jquery-shield', ['jquery', 'jquery-textcomplete', 'emojionearea'], function($){ return $ })
and then add require(...) your webpack bundle
Part 2: Using EmojiOne with Vue
As the OP mentioned his/her case is to use emojis in a chat app, I would also explain the solution with that case, though this can (and should) be modified for other use cases too!
The solution focuses on two aspects. First is to simply display the emoji in a message component, i.e., no need to display an emoji picker dialog and second is to display a emoji picker dialog whilst the user is typing into (say) a textarea.
To achieve the first goal, you can use a message component like,
Vue.component('message', {
props: ['content'],
render: function(h){
return h('div', {
class: { 'message': true }
}, [
h('div', {
class: { 'bubble': true },
domProps: {
innerHTML: emojione.toImage(this.content)
}
})
])
}
})
This would create a <message /> component which you can use as
<message content="Hey :hugging:!!" />
which will render
<div class="message">
<div class="bubble">
Hey <img class="emojione" alt="🤗" title=":hugging:" src="...">!!
</div>
</div>
Now, to create a <message-box /> component that will display an emoji picker to assist in autocomplete, do as follows
Vue.component('message-box', {
template: `<div class="message-box"><textarea></textarea></div>`,
mounted(){
// find the input
$(this.$el).find('textarea').emojioneArea()
}
})
And that's it! Although it may seem like a lot, the crux of the solution is quiet simple! Just use emojione.toImage(str) to get a DOM string and apply it to the Vue component (you can also use v-html to do this but IMO render() is a bit more sleek!). And for the displaying the picker, you just call $(...).emojioneArea() on the <textarea /> once the component is mounted.
Make sure to checkout full code example at https://jsfiddle.net/riyaz_ali/5Lhex13n
I configured 'i-tab-pane': Tabpane but report error,the code is bellow:
<template>
<div class="page-common">
<i-tabs>
<i-tab-pane label="wx">
content
</i-tab-pane>
</i-tabs>
</div>
</template>
<script>
import {
Tabs,
Tabpane
} from 'iview'
export default{
name:"data-center",
data(){
return {msg: 'hello vue'}
},
components: {
'i-tabs' : Tabs,
'i-tab-pane': Tabpane
}
}
</script>
Error traceback:
[Vue warn]: Unknown custom element: <i-tab-pane> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <DataCenter> at src/views/dc/data-center.vue
<Index> at src/views/index.vue
<App> at src/app.vue
I have tried in the main.js to global configuration:
Vue.component("Tabpane", Tabpane);
but still do not work.
How to resolve this issue?
If you're using a component within a component (e.g. something like this in the Vue DOM):
App
MyComponent
ADifferentComponent
MyComponent
Here the issue is that MyComponent is both the parent and child of itself. This throws Vue into a loop, with each component depending on the other.
There's a few solutions to this:
1. Globally register MyComponent
vue.component("MyComponent", MyComponent)
2. Using beforeCreate
beforeCreate: function () {
this.$options.components.MyComponent = require('./MyComponent.vue').default
}
3. Move the import into a lambda function within the components object
components: {
MyComponent: () => import('./MyComponent.vue')
}
My preference is the third option, it's the simplest tweak and fixes the issue in my case.
More info: Vue.js Official Docs — Handling Edge Cases: Circular References Between Components
Note: if you choose method's 2 or 3, in my instance I had to use this method in both the parent and child components to stop this issue arising.
Since you have applied different name for the components:
components: {
'i-tabs' : Tabs,
'i-tab-pane': Tabpane
}
You also need to have same name while you export: (Check to name in your Tabpane component)
name: 'Tabpane'
From the error, what I can say is you have not defined the name in your component Tabpane. Make sure to verify the name and it should work fine with no error.
Wasted almost one hour, didn't find a solution, so I wanted to contribute =)
In my case, I was importing WRONGLY the component.. like below:
import { MyComponent } from './components/MyComponent'
But the CORRECT is (without curly braces):
import MyComponent from './components/MyComponent'
One of the mistakes is setting components as array instead of object!
This is wrong:
<script>
import ChildComponent from './ChildComponent.vue';
export default {
name: 'ParentComponent',
components: [
ChildComponent
],
props: {
...
}
};
</script>
This is correct:
<script>
import ChildComponent from './ChildComponent.vue';
export default {
name: 'ParentComponent',
components: {
ChildComponent
},
props: {
...
}
};
</script>
Note: for components that use other ("child") components, you must also specify a components field!
For recursive components that are not registered globally, it is essential to use not 'any name', but the EXACTLY same name as your component.
Let me give an example:
<template>
<li>{{tag.name}}
<ul v-if="tag.sub_tags && tag.sub_tags.length">
<app-tag v-for="subTag in tag.sub_tags" v-bind:tag="subTag" v-bind:key="subTag.name"></app-tag>
</ul>
</li>
</template>
<script>
export default {
name: "app-tag", // using EXACTLY this name is essential
components: {
},
props: ['tag'],
}
I had this error as well. I triple checked that names were correct.
However I got this error simply because I was not terminating the script tag.
<template>
<div>
<p>My Form</p>
<PageA></PageA>
</div>
</template>
<script>
import PageA from "./PageA.vue"
export default {
name: "MyForm",
components: {
PageA
}
}
Notice there is no </script> at the end.
So be sure to double check this.
If you have path to the component (which causes a cycle) to index.js, cycle will be begin. If you set path directly to component, cycle will be not. For example:
// WRONG:
import { BaseTable } from #/components/Base'; // link to index.js
// SUCCESS:
import BaseTable from #/components/Base/Table.vue';
I had this error and discovered the issue was because the name of the component was identical to the name of a prop.
import Control from '#/Control.vue';
export default {
name: 'Question',
components: {
Control
},
props: ['Control', 'source'],
I was using file components. I changed the Control.vue to InputControl.vue and this warning disappeared.
The high votes answer is right. You can checkout that you have applied different name for the components. But if the question is still not resolved, you can make sure that you have register the component only once.
components: {
IMContainer,
RightPanel
},
methods: {},
components: {
IMContainer,
RightPanel
}
we always forget that we have register the component before
This is very common error that we face while starting any Project Vue. I spent lot of time to search this error and finally found a Solution.
Suppose i have component that is "table.vue",
i.e components/table.vue
In app.js
Vue.component('mytablecomp', require('./components/table.vue').default);
So in in your index.blade file call component as
<mytablecomp></mytablecomp>
Just you need to keep in mind that your component name is in small not in large or camel case.
Then my above code will surely work for you.
Thanks
We've struggled with this error twice now in our project with different components. Adding name: "MyComponent" (as instructed by the error message) to our imported component did not help. We were pretty sure our casing was correct, as we used what is in the documentation, which worked fine for the other 99% of our components.
This is what finally worked for us, just for those two problematic components:
Instead of this (which, again, works for most of our components):
import MyComponent from '#/components/MyComponent';
export default {
components: {
MyComponent
}
We changed it to ONLY this:
export default {
components: {
MyComponent: () => import('#/components/MyComponent')
}
I can't find the documentation we originally found for this solution, so if anyone has any references, feel free to comment.
If you are using Vue Class Component, to register a component "ComponentToRegister" you can do
import Vue from 'vue'
import Component from 'vue-class-component'
import ComponentToRegister from '#/components/ComponentToRegister.vue'
#Component({
components: {
ComponentToRegister
}
})
export default class HelloWorld extends Vue {}
Adding my scenario. Just in case someone has similar problem and not able to identify ACTUAL issue.
I was using vue splitpanes.
Previously it required only "Splitpanes", in latest version, they made another "Pane" component (as children of splitpanes).
Now thing is, if you don't register "Pane" component in latest version of splitpanes, it was showing error for "Splitpanes". as below.
[Vue warn]: Unknown custom element: <splitpanes> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
In my case it was the order of importing in index.js
/* /components/index.js */
import List from './list.vue';
import ListItem from './list-item.vue';
export {List, ListItem}
and if you use ListItem component inside of List component it will show this error as it is not correctly imported. Make sure that all dependency components are imported first in order.
This is WRONG:
import {
Tabs,
Tabpane
} from 'iview'
This is CORRECT:
import Iview from "iview";
const { Tabs, Tabpane} = Iview;
In my case (quasar and command quasar dev for testing), I just forgot to restart dev Quasar command.
It seemed to me that components was automatically loaded when any change was done. But in this case, I reused component in another page and I got this message.
Make sure that the following are taken care of:
Your import statement & its path
The tag name of your component you specified in the components {....} block
i ran into this problem and below is a different solution. I were export my components as
export default {
MyComponent1,
MyComponent2
}
and I imported like this:
import { MyComponent1, MyComponent2} from '#/index'
export default {
name: 'App',
components: {
MyComponent1,
MyComponent2
},
};
And it gave this error.
The solution is:
Just use export { ... } don't use export default
In my case, i was calling twice the import...
#click="$router.push({ path: 'searcherresult' })"
import SearcherResult from "../views/SearcherResult"; --- ERROR
Cause i call in other component...
The error usually arises when we have used the Component (lets say VText) but it has not been registered in the components declaration of the Parent Component(lets say Component B).
The error is more likely to occur when using components in a recursive manner. For example using tag=VText in an tag, as importing the component in a such case will result in error from Eslint as the component is not directly being used in the template. While not importing the component will cause an error in the console saying the component has not been registered.
In this case, it is a better approach to suppress the ESLinter on registration line of the Component(VText in this case). This suppression is done through writing // eslint-disable-next-line vue/no-unused-components
Example code is below
<template>
<i18n path="AssetDict.Companies" tag="VText">
<template>
<span class="bold-500">Hi This is a text</span>
</template>
</i18n>
</template>
<script>
import { VButton, VIcon, VTooltip, VText } from 'ui/atoms'
export default {
name: 'ComponentB',
components: {
VButton,
VIcon,
CompaniesModifyColumn,
VTooltip,
// eslint-disable-next-line vue/no-unused-components
VText,
},
}
</script>
I just encountered this. Easy solution when you know what to look for.
The child component was the default export in it's file, and I was importing using:
import { child } from './filename.vue'
instead of
import child from './filename.vue'.
What happened to me was I had correctly registered the component in components but I had another components key defined at the bottom of my component, so I had two components definitions and it looked like the latter one overrode the previous one. Removing it made it work.
I encounter same error msg while using webpack to async load vue component.
function loadVMap() {
return import(/* webpackChunkName: "v-map" */ './components/map.vue')
.then(({ default: C }) => {
Vue.component('ol-map',C);
return C;
})
.catch((error) => 'An error occurred while loading the map.vue: '+error);
}
I found that the then function never executed.
so I reg this component out of webpack import
import Map from './components/map.vue'
Vue.component('ol-map',Map);
Then I could gain the detailed error msg which said I used a var which is not imported yet.
I ran into this problem when:
I had components defined twice.
Used component instead of components.
I hope this helps others.
The question has been answered very well by #fredrivett here, but I wanted to add some context for other encountering the Circular Reference error when dealing with variables in general.
This error happens with any exported object not just components.
Exporting a variable from parent and importing it in a nested child:
🌐 EXAMPLE
<script>
// parent
export const FOO = 'foo';
...
</script>
❌ WRONG
<script>
// child
import { FOO } from 'path/to/parent'
export default {
data() {
return {
FOO
}
}
}
</script>
✅ CORRECT
<script>
// child
export default {
data() {
return {
FOO: require('path/to/parent').FOO
}
}
}
</script>
Note: in case you are dealing with objects you might want to create a global state which might serve you better.
I'm curious to know if this approach makes sense or it's an anti pattern.
In my case the child component name was "ABCChildComponent" and I was referring in the HTML as assuming it to work correctly. But, the correct name should be or . Hence, changed the name to "AbcChildComponent" and referring in the HTML works fine.
WRONG WAY :
import completeProfile from "#/components/modals/CompleteProfile";
export default {
components: completeProfile
};
RIGHT WAY :
import completeProfile from "#/components/modals/CompleteProfile";
export default {
components: {completeProfile} // You need to put the component in brackets
};