How to fix 'Cannot find module' in a vuejs module with npm link - vue.js

I've created a vuejs library with some components that could be used in many project.
In that library, I've got a component which can load svg files to be used inline in html (svg-icon).
It work great.
But in this same library, I've got another component which use svg-icon with a svg image stored in the library.
An import point, I'd like to use this library (node module) with an npm link
Which is the good way to give the path of the svg image, or where to store it?
I've tried a lot of different paths, but none of them is working...
This is my svg-icon component:
<template>
<component :is="component"></component>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
icon: {
type: String,
required: true
}
},
data () {
return {
component: null
}
},
watch: {
icon: () => {
this.load()
}
},
computed: {
loader () {
return () => import(this.icon)
}
},
methods: {
load () {
this.loader().then(() => {
this.component = () => this.loader()
})
}
},
mounted () {
this.load()
}
}
</script>
And this is the component which use svg-icon (the svg image is in the same folder actually) :
<template>
<svg-icon icon="~my-module/components/media/no-image.svg"></svg-icon>
<svg-icon icon="./no-image.svg"></svg-icon>
</template>
<script>
import SvgIcon from '../svg-icon/SvgIcon'
export default {
components: {
SvgIcon
}
}
</script>
I always got this errors:
Cannot find module '~my-module/components/media/no-image.svg'
Cannot find module './no-image.svg'
Which is the good path in that situation? Or should I put the svg file somewhere else? (I'd like to keep it in the library)

I've created a CodeSandbox here
SvgIcon.vue
<template>
<span v-html="icon"></span>
</template>
<script>
export default {
name: "SvgIcon",
props: {
icon: {
type: String,
required: true
}
}
};
</script>
HelloWorld.vue
//Usage
<template>
<svg-icon :icon="AlertIcon"></svg-icon>
</template>
<script>
import AlertIcon from "../assets/alert.svg";
import SvgIcon from "./SvgIcon";
export default {
data() {
return { AlertIcon };
},
components: {
SvgIcon
}
};
</script>
I've made some changes to your components.
You need to import the image and pass it to your component because dynamic import causes complications when it comes to the absolute paths.
I've removed some unnecessary fields from your SvgIcon code.
Hope this helps.

Related

Ckeditor4-vue with Nuxt.js how to access CKEDITOR

I have started using ckeditor4-vue
https://ckeditor.com/docs/ckeditor4/latest/guide/dev_vue.html#using-the-component-locally
<template>
<div>
<ckeditor
v-model="formData"
:config="editorConfig"
#input="$emit('update:editorData', formData)"
></ckeditor>
</div>
</template>
<script>
import CKEditor from 'ckeditor4-vue';
export default {
components: {
ckeditor: CKEditor.component
},
props: ['editorData'],
data() {
return {
formData: this.editorData,
editorConfig: {}
};
},
mounted() {
CKEDITOR.disableAutoInline = true;
}
};
</script>
However I can't find how I can use CKEDITOR, in this example, I have CKEDITOR is not defined.
Thank you
make sure you're calling it the right way, CKEDITOR <> CKEditor.
try removing your mounted and this should works

Nuxt JS load components depending on API response

I'm building a nuxt app to consume the wp rest API. In my fetch method I fetch information about needed components. I can't figure out how to then import all the components and render them. I've tried several methods, but I can't see to make it work.
Here's what works:
<component :is="test" :config="componentList[0]"></component><br>
export default {
async fetch({ store, $axios }) {
await store.dispatch("getPageBySlug", "home");
},
computed: {
test() {
return () => import('~/components/HeroIntro');
}
}
};
Ok so this is easy, nothing special - I could now import the component based on the slug etc. But I need to render multitple components and therefor im doing this:
<component
v-for="component in componentList"
:key="component.acf_fc_layout"
:is="component.acf_fc_layout"
:config="component">
</component>
along with this
export default {
async fetch({ store, $axios }) {
await store.dispatch("getPageBySlug", "home");
},
computed: {
page() {
return this.$store.getters.getPageBySlug("home");
},
componentList() {
return this.page.acf.flexible_content;
},
componentsToImport() {
for(const component of this.componentList) {
() => import('~/components' + component.acf_fc_layout);
}
}
}
};
All I'm getting is
Unknown custom element: HeroIntro - did you register the
component correctly? For recursive components, make sure to provide
the "name" option
How do I archieve what im trying?
edit:
So, after a lot of trying, I could only make it work with using an extra component, "DynamicComponent":
<template>
<component :is="componentFile" :config="config"></component>
</template>
<script>
export default{
name: 'DynamicComponent',
props: {
componentName: String,
config: Object
},
computed: {
componentFile() {
return () => import(`~/components/${this.componentName}.vue`);
}
}
}
</script>
Now in Index.vue
<template>
<main class="container-fluid">
<DynamicComponent
v-for="(component, index) in componentList"
:key="index"
:componentName="component.name"
:config="component"
/>
</main>
</template>
<script>
export default {
components: {
DynamicComponent: () => import("~/components/base/DynamicComponent")
}
I am not sure yet if this is optimal - but for now it works great - any input / opinions would be great!

Vue.js single file component 'name' not honored in consumer

Please pardon my syntax, I'm new to vue.js and may not be getting the terms correct.
I've got a single file component (SFC) named CreateTodo.vue. I've given it the name 'create-todo-item' (in the name property). When I import it in my app.vue file, I can only use the component if I use the markup <create-todo>. If I use <create-todo-item>, the component won't render on the page.
I've since learned that I can do what I want if I list the component in my app.vue in the format components: { 'create-todo-item': CreateTodo } instead of components: { CreateTodo }.
My question is this: is there any point to giving the component a name in the name property? It's not being honored in the consumer, and if I leave it empty, the app runs without error.
Also, am I correct in my belief that vue-loader is assigning the kebab-case element name for template use based on the PascalCase import statement?
Bad - component name property
Here's the code where I try to name the SFC (CreateTodo.vue)
<script>
export default {
name: 'create-todo-item',
data() {
return {
titleText: '',
projectText: '',
isCreating: false,
};
},
};
</script>
The name as listed in the component is ignored by my App.vue. The html renders fine even though I have the element <create-todo> instead of <create-todo-item>:
<template>
<div>
<!--Render the TodoList component-->
<!--TodoList becomes-->
<todo-list v-bind:todos="todos"></todo-list>
<create-todo v-on:make-todo="addTodo"></create-todo>
</div>
</template>
<script>
import TodoList from './components/TodoList.vue'
import CreateTodo from './components/CreateTodo.vue'
export default {
name: 'app',
components: {
TodoList,
CreateTodo,
},
// data function avails data to the template
data() {
return {
};
},
methods: {
addTodo(todo) {
this.todos.push({
title: todo.title,
project: todo.project,
done: false,
});
},
}
};
</script>
Good - don't use component name property at all
Here's my CreateTodo.vue without using the name property:
<script>
export default {
data() {
return {
titleText: '',
projectText: '',
isCreating: false,
};
},
};
</script>
And here's my App.vue using the changed component:
<template>
<div>
<!--Render the TodoList component-->
<!--TodoList becomes-->
<todo-list v-bind:todos="todos"></todo-list>
<create-todo-item v-on:make-todo="addTodo"></create-todo-item>
</div>
</template>
<script>
import TodoList from './components/TodoList.vue'
import CreateTodo from './components/CreateTodo.vue'
export default {
name: 'app',
components: {
TodoList,
'create-todo-item': CreateTodo,
},
// data function avails data to the template
data() {
return {
};
},
methods: {
addTodo(todo) {
this.todos.push({
title: todo.title,
project: todo.project,
done: false,
});
},
}
};
</script>
First note that the .name property in a SFC module is mostly just a convenience for debugging. (It's also helpful for recursion.) Other than that, it doesn't really matter when you locally register the component in parent components.
As to the specific details, in the first example, you're using an ES2015 shorthand notation
components: {
TodoList,
CreateTodo,
},
is equivalent to
components: {
'TodoList': TodoList,
'CreateTodo': CreateTodo
},
so that the component that is imported as CreateTodo is given the name 'CreateTodo' which is equivalent to <create-todo>.
In the second example, you give the name explicitly by forgoing the shorthand notation
components: {
TodoList,
'create-todo-item': CreateTodo,
},
That's equivalent, btw to
components: {
TodoList,
'CreateTodoItem': CreateTodo,
},
So you can see, in that case, that you're giving the component the name 'CreateTodoItem' or, equivalently, <create-todo-item>

Why this.$listeners is undefined in Vue JS?

Vue.js version: 2.4.2
Below component always print this.$listeners as undefined.
module.exports = {
template: `<h1>My Component</h1>`,
mounted() {
alert(this.$listeners);
}
}
I register the component and put it inside a parent component.
Can someone tell me why?
You have to understand what $listeners are.
this.$listeners will be populated once there are components that listen to events that your components is emitting.
let's assume 2 components:
child.vue - emits an event each time something is written to input field.
<template>
<input #input="emitEvent">
</input>
</template>
<script>
export default {
methods: {
emitEvent() {
this.$emit('important-event')
console.log(this.$listeners)
}
}
}
</script>
parent.vue - listen to the events from child component.
<template>
<div class="foo">
<child #important-event="doSomething"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
data() {
return {
newcomment: {
post_id: 'this is default value'
}
}
},
components: { child },
methods: {
doSomething() {
// do something
}
}
}
</script>
With this setup, when you type something to the input field, this object should be written to the console:
{
`important-event`: function () { // some native vue.js code}
}
I added the following alias to my webpack.config.js file and this resolved the issue for me:-
resolve: {
alias: {
'vue$': path.resolve(__dirname, 'node_modules/vue/dist/vue.js')
}
},

How to use helper functions for imported modules in vuejs .vue template?

I have a helper.js file with contains:
module.exports = {
getSrmColor: (color) => {
return color;
}
}
My .vue file has:
<template>
<div>
{{ recipeHelper.getSrmColor(recipe.color) }}
</div>
</template>
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: ["recipe"]
}
</script>
I get the following error:
Property or method "recipeHelper" is not defined on the instance but referenced during render.
Make sure to declare reactive data properties in the data option.
Make new helper instance inside your vue component, like below.
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: [
"recipe"
],
mounted: function() {
this.recipeHelper = recipeHelper;
}
}
</script>
I think you need to create "data value" for your import value. Could you try something like that:
<script>
import recipeHelper from "./helpers.js";
export default {
name: "Recipe",
props: ["recipe"],
data: function() {return {
recipeHelper: recipeHelper
}}
}
</script>