In element-plus we already have icons for example <i-mdi-edit />
how can use these icons so i can create my own component
<Icon name="edit" />
and i pass the name property so i can get the icon
my Icon component as follows;
<script setup lang="ts">
import { ref } from "vue";
let props = withDefaults(defineProps<{ name: string }>(), {
name: "edit",
});
let component = ref(`i-mdi-${props.name}`);
</script>
<template>
<component :is="component" />
</template>
You can use dynamic components for that.
For example, in your case, and assuming you have the name prop registered, you can use something like:
<component :is="`i-mdi-${name}`"></component>
Edit: Since you mentioned element plus, here is a working example. However, in this case, it seems a bit redundant to do like you want.
Related
I have this simple file:
<script setup>
import { ref } from 'vue'
import TheHeader from '#/components/_headerbar/TheHeader.vue'
import TheSidebar from '#/components/_sidebar/TheSidebar.vue'
const sidebarState = ref(false)
const onSidebarToggle = () => {
sidebarState.value = !sidebarState.value
}
</script>
<template>
<QLayout view="hHh lpR fFf">
<TheHeader #toggle-sidebar="onSidebarToggle" />
<TheSidebar :sidebar-state="sidebarState.value" />
<QPageContainer>
<RouterView v-slot="{ Component }">
<component :is="Component" />
</RouterView>
</QPageContainer>
</QLayout>
</template>
The sidebarState variable here updates just fine everytime the event toggle-sidebar is fired, but the prop that recieve its value never updates and I just don't know what is happening.
This is the TheSidebar.vue file:
<script setup>
const props = defineProps({
sidebarState: {
type: Boolean,
default: true
}
})
</script>
<template>
<QDrawer
:model-value="props.sidebarState"
show-if-above
side="left"
bordered
>
content
</QDrawer>
</template>
Debugging here I can tell the sidebarState prop from TheSidebar.vue file just never changes, even though the data prop of TheHeader.vue sidebarState changes just normally.
What am I doing wrong?
You shouldn't use .value in template with refs of top-level properties. The value is automatically unwrapped for you (note: make sure the toggle in the top left of the docs is switched from "Options" to "Composition" for link to correctly work).
Simply remove .value and your code should work
<TheSidebar :sidebar-state="sidebarState" />
I have a component which gathers data from an API. The data brought back from the API is an array with details in. One of the values in the array is the type of component which should be rendered, all other data is passed through to the component.
I'm trying to render the correct component based of the value brought back from the database, but it is sadly not working.
I'm new to Vue but had it working in vue2 but would like it to work in Vue 3 using the composition API.
this is my component code which I want to replace:
<component :is="question.type" :propdata="question" />
When viewed within the browser this is what is actually displayed, but doesn't use the SelectInput component:
<selectinput :propdata="question"></selectinput>
SelectInput is a component with my directory, and works as intended if I hard code the :is value, like below:
<component :is="SelectInput" propdata="question" />
my full component which calls the component component and swaps components:
<template>
<div class="item-group section-wrap">
<div v-bind:key="question.guid" class='component-wrap'>
<div class="component-container">
<!-- working -->
<component :is="SelectInput" :propData="question" />
<!-- not working -->
<component v-bind:is="question.type" :propData="question" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, toRefs } from 'vue';
import SelectInput from "./SelectInput";
import TextareaInput from "./TextareaInput";
const props = defineProps({
question: Object,
});
const { question } = toRefs(props);
</script>
In case that your Components filenames are equal to the type specifier on your question Object, then you could dynamically import them to save some code lines.
This would also result in better scalability since you don't have to touch this component anymore in case you create more types.
<template>
<div class="item-group section-wrap">
<div v-bind:key="question.guid" class='component-wrap'>
<div class="component-container">
<!-- working -->
<component v-bind:is="getComponent(question.type)" :propData="question" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, toRefs } from 'vue';
const props = defineProps({
question: Object,
});
// GIVEN THE question.types are equal to the fileNames of the components to render:
const getComponent = (name) => import(`./${name}.vue`);
const { question } = toRefs(props);
</script>
Figured out that because I'm using script setup the components aren't named so the component component doesn't know which component to render.
so, i created an object with the components and a reference to the component:
const components = {
'SelectInput': SelectInput,
'TextareaInput': TextareaInput
};
and another function which takes in the component i want to show and links it to the actual component:
const component_type = (type) => components[type];
then in the template i call the function and the correct component is rendered:
<component v-bind:is="component_type(question.type)" :propData="question" />
complete fixed code:
<template>
<div class="item-group section-wrap">
<div v-bind:key="question.guid" class='component-wrap'>
<div class="component-container">
<!-- working -->
<component v-bind:is="component_type(question.type)" :propData="question" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, toRefs } from 'vue';
import SelectInput from "./SelectInput";
import TextareaInput from "./TextareaInput";
const props = defineProps({
question: Object,
});
const components = {
'SelectInput': SelectInput,
'TextareaInput': TextareaInput
};
const component_type = (type) => components[type];
const { question } = toRefs(props);
</script>
Not too sure if this is the correct way of doing this but it now renders the correct component.
I want to make dynamic components fetched from an array. I tried using <component></component> to achieve this, but it didn't render anything. When I inspect it in the browser, the component is rendered like below:
<test></test>
My code is,
<template>
<component v-for="(section, index) in compo" :key="section+index" v-bind:is="section" title="Gd" />
<template>
<script>
import Test from "#/Components/Test.vue";
export default {
data() {
return {
compo: ['Test']
}
},
};
</script>
I have a Vue wrapper for Select2 component that I wrote a while ago. The wrapper is able to access the bind:class and bind:style attributes to pass along to the Select2 library, like so:
this.$vnode.data.staticClass // a string value of class="" attribut
this.$vnode.data.class // an Object passed in via bind:class="{}" attribute
this.$vnode.data.style // same as above but for style
this.$vnode.data.staticStyle
Now I would really like to be able to detect changes to these, so that I could pass them along to Select2 innards.
How can this be done?
This is one variant for a functional wrapper around Select2:
<template functional>
<component
:is="injections.components.Select2"
:ref="data.ref"
class="any_desired_static_classes"
:class="[
data.class,
data.staticClass,
]"
:style="[
data.style,
data.staticStyle,
]"
v-bind="data.attrs"
v-on="listeners"
>
<slot />
</component>
</template>
<script>
import Select2 from 'select2';
export default
{
name: 'SelectWrapper',
inject:
{
// workaround since functional components are stateless and can not register components in the normal way
components:
{
default:
{
Select2
}
}
}
};
</script>
I'm trying to dynamically show a footer/header based on a child view in ionic
I am using the Ionic 4 framework in combination with Vue.js. Tried slots and such having the feeling i'm on the right track but not fully there yet.
I've got a Base.vue (component) which holds
<template>
<ion-app>
<ion-page class="ion-page" main>
<page-header />
<router-view />
<page-footer />
</ion-page>
</ion-app>
</template>
<script>
import PageHeader from '#/components/PageHeader'
import PageFooter from '#/components/PageFooter'
import { mapState } from 'vuex'
export default {
name: 'master',
components: {
PageHeader,
PageFooter,
},
}
</script>
As a child view i've got the following; i know that it's not the right approach to include it inside the <ion-content> but don't know how to set this up in the correct way:
<template>
<ion-content fullscreen>
<page-header>
<ion-toolbar>
<ion-title>
Test
</ion-title>
</ion-toolbar>
</page-header>
<ion-content>
<p>Schedule page</p>
</ion-content>
</ion-content>
</template>
<script>
import PageHeader from '#/components/PageHeader'
export default {
name: 'schedule',
components: {
PageHeader,
},
}
</script>
The header component (which should be dynamic):
<template>
<ion-header>
<slot name="header" />
</ion-header>
</template>
<script>
export default {
name: 'page-header',
}
</script>
What i'm trying to do is making a Base.vue with a dynamic header (PageHeader.vue) so based on a given child view i could change or extend the header if needed.
So I think you're saying you want to change the content of the page header depending on the child.
Components cannot directly affect the templates of other components in the tree. Slots give you some control over this, but it is limited to allowing a component to inject templates into sections of a child component, not the other way around.
Your options are:
Add logic to your parent component which detects what child component is shown and then change the page header accordingly. The page header won't be controlled directly by the child component, though.
Use named views with vue-router.
Use something like portal-vue, but don't go crazy with this kind of power...