Dynamically importing SVGs with nuxtjs/svg - vue.js

Using nuxtjs/svg package, I'm conditionally rendering inline SVGs thus:
<ArrowRight v-if="condition" />
<ExternalLink v-else />
Script:
import ArrowRight from '~/assets/img/arrow-right.svg?inline'
import ExternalLink from '~/assets/img/external-link.svg?inline'
export default {
components: {
ArrowRight,
ExternalLink
}
}
I'd like to make these imports dynamically, but I don't know how in this case partly because of the necessity of the "?inline" part when importing the SVG.
Any idea as to how I can import the SVGs dynamically?

<template>
<div v-html="require(`~/assets/img/${image}.svg?raw`)"/>
</template>
<script>
export default {
computed: {
image() {
return condition ? 'arrow-right' : 'external-link'
}
}
}
</script>
That's one way by using SVGs as raw. But the idea should be clear ;-)

(copied from nuxt svg module readme)
To dynamically import an SVG, you can use the inline require() syntax.
<template>
<div v-html="require(`../assets/${name}.svg?raw`)" />
</template>
<script>
export default {
props: {
name: { type: String, default: "image" },
},
};
</script>
To render an SVG without wrapper element and the use of v-html, a combination of dynamic components and ?inline can be used.
<template>
<component :is="require(`../assets/${name}.svg?inline`)" />
</template>
<script>
export default {
props: {
name: { type: String, default: "image" },
},
};
</script>

Related

How to pass images url through props in vue.js

For some reason it is not working, is this correct?
Parent Component:
<achievement-post imgURL="../assets/ach1.jpg"></achievement-post>
<script>
import achievementPost from "../components/AchievementPost.vue";
// ...
</script>
Child component:
<template>
<img :src="imgURL" />
</template>
<script>
export default {
props: {
imgURL: String
},
// ...
}
</script>
When I hardcode the URL it works, so I'm not sure what is going on. Please help.
If you are using Webpack, there is no way for it to track the image module dependency with a string url prop, so it will not try and resolve it.
The solution is to require it with an expression.
A context is created if your request contains expressions, so the
exact module is not known on compile time.
Child component:
<template>
<img :src="require(`../assets/${imgURL}`)" />
</template>
<script>
export default {
props: {
imgURL: String
},
// ...
}
</script>
Parent component
<achievement-post imgURL="ach1.jpg"></achievement-post>
<script>
import achievementPost from "../components/AchievementPost.vue";
// ...
</script>
HTML attributes are case insensitive: You should avoid using capital letters in props names. Maybe it's the cause of your problem.
<achievement-post img_url="../assets/ach1.jpg"></achievement-post>
And
export default {
props: {
img_url: String
},
// ...
}
You can see it at the bottom of this page:
https://vuejs.org/2016/02/06/common-gotchas/
or at the top of this page:
https://v2.vuejs.org/v2/guide/components-props.html

Vuejs build/render component inside a method and output into template

I have a string (example, because it's an object with many key/values, want to loop and append to htmloutput) with a component name. Is it possible to render/build the component inside a method and display the html output?
Is that possible and how can i achieve that?
<template>
<div v-html="htmloutput"></div>
</template>
<script>
export default {
component: {
ComponentTest
},
data() {
return {
htmloutput: ''
}
},
methods:{
makeHtml(){
let string = 'component-test';//ComponentTest
//render the ComponentTest directly
this.htmloutput = ===>'HERE TO RENDER/BUILD THE COMPONENTTEST'<==
}
},
created(){
this.makeHtml();
}
</script>
You might be looking for dynamic components:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Example:
<template>
<component :is="changeableComponent">
</component>
</template>
<script>
import FirstComponent from '#/views/first';
import SecondComponent from '#/views/second';
export default {
components: {
FirstComponent, SecondComponent
},
computed: {
changeableComponent() {
// Return 'first-component' or 'second-component' here which corresponds
// to one of the 2 included components.
return 'first-component';
}
}
}
</script>
Maybe this will help - https://forum.vuejs.org/t/how-can-i-get-rendered-html-code-of-vue-component/19421
StarRating is a sample Vue component. You can get it HTML code by run:
new Vue({
...StarRating,
parent: this,
propsData: { /* pass props here*/ }
}).$mount().$el.outerHTML
in Your method. Remember about import Vue from 'vue'; and of course import component.
What you're trying to do really isn't best practice for Vue.
It's better to use v-if and v-for to conditionally render your component in the <template> section.
Yes you can use the render function for that here is an example :
Vue.component('CompnentTest', {
data() {
return {
text: 'some text inside the header'
}
},
render(createElement) {
return createElement('h1', this.text)
}
})
new Vue({
el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<Compnent-test />
</div>
Or :
if you are using Vue-cli :
on your componentTest component :
export default {
render(createElement) {
return createElement('h1', 'sometext')
}
// Same as
// <template>
// <h1>sometext</h1>
// </template>
//
}
and on your root element (App.vue as default) :
export default {
....
component: {
ComponentTest
}
}
<template>
<div>
....
<Component-test />
</div>
</template>
example : codesandbox
you can read more about
Render Functions & JSX

How to fix 'Cannot find module' in a vuejs module with npm link

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.

How can I pass parameters from vue custom element to a vue component?

I don't have a whole vue app, so I use custom elements to replace some elements that should be handled with vue.
I simply want to use the vue multiselect plugin in a html file.
So I tried the following:
index.ts
import Vue from "vue"
import VueCustomElement from 'vue-custom-element'
import Autocomplete from "./vue/autocomplete.vue"
Vue.use(VueCustomElement);
Vue.customElement('auto-complete', Autocomplete);
test.html
<auto-complete
v-model="value"
:options="options"
placeholder="test"
#search-change="getData"
>
</auto-complete>
test.vue
<template>
<multiselect v-model="value" :options="options" #search-change="getData"></multiselect>
</template>
<script type="ts">
const Multiselect = require('vue-multiselect').default
export default {
components: { Multiselect },
data () {
return {
value: 'test',
options: ['list', 'of', 'options']
}
},
methods: {
getData (query) {
console.log(123)
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
In the output the data of the custom element is always ignored and only the parameters in the part in the .vue file is used.
How can i achieve that the parameters like placeholder or #search-change are used from the custom element?
I am also using vue-custom-elements in one of my projects.
You are passing option as props so you need to add it as a prop in your autocomplete.vue.
<template>
<multiselect v-model="value" :options="options" #search-change="getData"></multiselect>
</template>
<script type="ts">
const Multiselect = require('vue-multiselect').default
export default {
props: ['options'],
components: { Multiselect },
data () {
return {
value: 'test'
}
},
methods: {
getData (query) {
console.log('123')
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

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>