I'm passing a string containing a sequence of elements as a prop, which I try to render using :v-html="prop":
<template>
<svg
version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 26 26"
:class="sizeClass"
:height="size"
:width="size"
>
<g :v-html="sequence" />
</svg>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
name: "Avatar",
props: {
sizeClass: String,
size: String,
sequence: String,
},
});
</script>
However inspecting the result on dev tools, the html is not rendered, but appears only in the v-html attribute :
What am I missing ?
directives are bound by default they don't need binding sign : or v-bind::
<g v-html="sequence" />
Related
I have the following code, that prints the menu Icon but doesn't adapt to the size of the screen:
<template>
<menuIcon />
</template>
<script setup>
let menuIcon = <svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg>;
</script>
Previously you could do the following:
<script>
import menuIcon from '#assets/Icons/bars.svg';
export default {
name: "Navigation",
components: {
menuIcon,
},
};
</script>
And it worked properly; where is the best option to use script setup to use an svg image?
thanks in advance!
I'm trying to use svg image in script setup cpnfiguration.
I am using Quasar to build my Vue app and I want to create a base component (a.k.a. presentational, dumb, or pure component) using q-input.
I have a created a SFC named VInput.vue as my base component, it looks like this:
<template>
<q-input
outlined
class="q-mb-md"
hide-bottom-space
/>
</template>
Then I created a SFC named TestForm.vue that looks like this:
<template>
<q-form>
<v-input label="Email" v-model="email" />
</q-form>
</template>
<script setup lang="ts">
import VInput from './VInput.vue';
import { ref } from 'vue';
const email = ref('john#example.com');
</script>
The label="Email" v-model="email" parts are passed down to my VInput.vue base component and correctly rendered on the page.
But there is a typescript error on q-input of the VInput.vue base component because q-input requires a v-model:
`Type '{ outlined: true; class: string; hideBottomSpace: true; "hide-bottom-space": boolean; }' is not assignable to type 'IntrinsicAttributes & VNodeProps & AllowedComponentProps & ComponentCustomProps & QInputProps'.`
`Property 'modelValue' is missing in type '{ outlined: true; class: string; hideBottomSpace: true; "hide-bottom-space": boolean; }' but required in type 'QInputProps'.ts(2322)`.
So how do I code the VInput.vue base component without knowing the v-model value head of time?
I have come up with the below solution, which seems to work because I think the v-model passed down is overiding the base component v-model.
But I wanted to ask to make sure I wasn't screwing something up.
Is this the correct way of doing things? It seems hacky.
<template>
<q-input v-model="inputText" outlined class="q-mb-md" hide-bottom-space />
</template>
<script setup lang="ts">
const inputText = '';
</script>
I found a couple of solutions:
Solution 1
It involves splitting the v-model into it seperate parts (:model-value and #update:model-value, and then passing in the text value as a prop.
Base component VInput.vue:
<template>
<q-input
outlined
class="q-mb-md"
hide-bottom-space
:model-value="text"
#update:model-value="(value) => emit('update:text', value)"
/>
</template>
<script setup lang="ts">
defineProps({
text: {
required: false,
type: String,
},
});
const emit = defineEmits(['update:text']);
</script>
Solution 2
Extracting the prop and using toRef on it.
<template>
<q-input outlined class="q-mb-md" hide-bottom-space v-model="textProp" />
</template>
<script setup lang="ts">
import { toRef } from 'vue';
const props = defineProps({
text: {
required: false,
type: String,
default: '',
},
});
const textProp = toRef(props, 'text');
</script>
I have configured Vite with an alias "#" as "./src".
Using the alias directly as <img>.src is ok:
<!-- this is ok -->
<img src="#/assets/icon-1.svg">
but passing the src as a prop is not working:
<!-- ComponentA -->
<template>
<img :src="imgSrc">
</template>
<!-- Parent Component: alias not resolved as expected; imgSrcWithAlias is "#/assets/icon-1.svg" -->
<component-a :img-src="imgSrcWithAlias" />
Is there any solution to use file path alias when passing props?
Asset URLs must be manually resolved in script with the import keyword:
<script setup>
import imgSrcWithAlias from '#/assets/icon-1.svg'
</script>
<template>
<component-a :img-src="imgSrcWithAlias" />
</template>
demo
#tony19 Answer was what works for me. Using Vue 3 with Vite + TypeScript (Js should work too!) The only difference, is that if aren't using a script with the "setup" attribute: You need to import the image, and return it inside the export default. See:
<script lang="ts">
import { defineComponent } from 'vue'
//Import the images:
import feedImg from './assets/img/feed.png'
export default defineComponent({
setup() {
//Return the imgs:
return {
feedImg
}
}
})
</script>
After that, you just need to bind the image to the prop on the component usage:
<ImageComponent
:imgSrc="feedImg"
alt="Videos on homepage"
/>
And load it on the component itself, binding the src on the img tag:
<template>
<img :alt="alt" :src="imgSrc" />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: {
imgSrc: {
type: String,
required: true
},
alt: {
type: String,
required: true
}
}
})
</script>
Please see this minimum example, I have a simple component called HelloWorld.vue
HelloWorld.vue
<template>
<div class="foo">bar</div>
</template>
When I using this component like this
App.vue
<template>
<HelloWorld />
</template>
<script>
import HelloWorld from "./HelloWorld.vue";
export default {
components: {
HelloWorld,
},
};
</script>
This rendered HTML looks like this
Rendered HTML
<div class="foo">bar</div>
However, when I add :is prop, rendered HTML changed
App.vue
<template>
<HelloWorld is="h2" />
</template>
<script>
import HelloWorld from "./HelloWorld.vue";
export default {
components: {
HelloWorld,
},
};
</script>
Rendered HTML
<h2></h2>
Why is this happening?
Is it possible to overwrite only the outer HTML tag just like the class and style prop?
is should be used together with the component element:
<component is="h2"></component>
<HelloWorld is="h2" /> efficiently renders h2 instead of HelloWorld.
In order for root element to be configurable, the component should provide this:
<template>
<component :is="tag" class="foo">bar</div>
</template>
<script>
export default {
props: {
tag: {
type: String,
default: 'div'
}
}
}
</script>
I have a component called legend that should embed SVG elements inside a template that places the actual SVG tags:
<svg xmlns="http://www.w3.org/2000/svg" class="pie-chart" :width="outerwidth" :height="outerheight">
<legend ref="legend" :series="series" :position="positionLegend" :options="{}" ></legend>
</svg>
But the legend component does not get rendered the output is:
<div id="graphbox">
<svg xmlns="http://www.w3.org/2000/svg" width="1691" height="14" class="pie-chart">
<legend series="[object Object],[object Object],[object Object]" position="[object Object]" options="[object Object]"></legend>
</svg>
</div>
Does vue not parse within SVG tags?
Components inside <svg> should work fine but you'll need to rename legend to something else. legend is a standard HTML element name so Vue will get confused.
Also make sure that you've registered the component correctly, either locally or globally. Usually Vue will warn you if you've forgotten to register a component but in this case it won't because legend is a standard HTML element.
Here's a complete example, similar to the one in the question:
const myLegend = {
template: `<circle r="10" cx="20" cy="20" :fill="color" />`,
props: ['color']
}
new Vue({
el: '#graphbox',
components: {
myLegend
},
data () {
return {
outerheight: 50,
outerwidth: 50,
color: '#ff0000'
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="graphbox">
<svg :width="outerwidth" :height="outerheight">
<my-legend :color="color"></my-legend>
</svg>
</div>