Dynamically assign directives via an object in vue - javascript

If I have a component in vue:
<template>
<component v-bind="dynamicAttrs"></component>
</template>
And I want to assign some dynamic attributes to it where v-demo is a custom directive:
data() {
return {
dynamicAttrs: {
'class': 'foo'
'v-demo': true
}
}
}
Although I can see the attbitues in the dom the custom directive is not firing in this scenario.
Is there a way I can dynamically assign directives via an object in vue?

v-bind is a directive. You can't apply a directive with another directive

It is true what Michael said but maybe you can work around it a bit:
<template>
<component :is="customComponentName" :data="dynamicAttrs"></component>
</template>
then you can create a custom component that renders based on the data you put in.
https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
option 2:
create an custom directive and put the dynamic values in the arguments:
<template>
<component v-demo:[show]="dynamicMethod" :class="{}" />
</template>
https://v2.vuejs.org/v2/guide/custom-directive.html#Dynamic-Directive-Arguments

You could do in this manner if you genuinely want this(Just recently, I had the same thing). Although it yikes a little, but it works. If you have more directives, it becomes ugly 😉
<template>
<component class="foo" v-if="hasDemoDirective" v-demo/>
<component class="foo" v-else/>
</template>
<script>
export default {
name: 'ComponentWrapper',
props: {
hasDemoDirective: {
type: Boolean,
default: false
}
}
}
</script>

Related

Vue 3 Composition API, Props, and v-if rendering despite false value

I have an issue I don't think I'm really understanding here. I have a child component included which passes an "active" prop in and can be set to true or false. The idea is if it's passed "true" then a part of the component shows, and if it's passed "false" it doesn't.
from what I understand, I should be able to just use the prop name and do something like:
<template>
<div v-if="active">this is the value of active: {{active}}</div>
</template>
The issue here is if I directly set v-if in the above statement to true or false, then it works as expected. If I pass it in as a prop, it always shows regardless of whether true or false.
Works (doesn't show anything):
<template>
<div v-if="false">this is the value of active: {{active}}</div>
</template>
Doesn't Work (contents in the div shows regardless of value of active):
//-File1---------------------------------------
<template>
<myComponent active=false />
</template>
//-File2---------------------------------------
<template>
<div v-if="active">this is the value of active: {{active}}</div>
</template>
<script>
export default{
props:['active']
}
</script>
Why is this? I confirmed, by displaying the value of "active" that it's passing in as false, but it's still rendering despite the value being false. Am I missing something here? I've tried playing with quotes, without quotes, passing it into a local value using ref and using that:
import { ref } from 'vue';
export default{
props:['active']
setup(props,ctx){
const active = ref(props.active);
return {
active
}
}
}
that also did not work.
It's because your prop is passed in as string from parent component (the default behavior like all other html properties). In order to pass in the prop as boolean, you need to use v-bind syntax or : for short so that false is evaluated as a javascript expression instead of string:
<template>
<myComponent v-bind:active="false" />
</template>
Or
<template>
<myComponent :active="false" />
</template>
on your export default,
props: {
active: {
type: Boolean,
default: false
}
}
on your component template,
<template>
<div v-if="active !== false"> show only when active {{ active }}</div>
</template>
when using the component, bind the active element to false
<myComponent :active="false" />

Modify child component data added via slots from parent component in vuejs

I'm a bit new to the component's world and trying to figure out a thing, how the parent child relationship works in components. I've seen some examples of some component libraries where they have some parent child components to be defined and those are used as the child components. For example, table and tr:
<my-table> <!-- Parent -->
<my-tr> </my-tr> <!-- Child -->
</my-table>
Now, I assume, that child works for parent via slots. So the parent should be defined something like this:
<template>
<div>
<slot></slot>
</div>
</template>
Now the parent element can have multiple <my-tr> as well. And slot should be rendering all of those. However, I am trying to a similar thing but a little more complex than that.
I am trying to create a slider with this approach. Where there is a my-slider component and my-slider-item components used to define inside my-slider component. And then I want to control the visibility of the child components defined in the parent component slot by modifying it's properties.
It should be looking like this:
<my-slider>
<my-slider-item>Item 1</my-slider-item>
<my-slider-item>Item 2</my-slider-item>
<my-slider-item>Item 3</my-slider-item>
</my-slider>
my-slider component
<template>
<div class="my-slider">
<slot></slot>
</div>
</template>
my-slider-item component
<template>
<div class="my-slider__item">
<slot></slot>
</div>
</template>
Now how can I know in the parent that how many <my-slider-item> are defined in the parent slot and based on that I want to control the visibility of the child 1 at a time as it is going to work as the slider.
I'm not sure but I missing some basic concept here which I was not getting after looking at tons of example since yesterday. If anyone can please help here? Thanks a lot!
The parent-child relationship is actually established by importing the child component into the parent component, and including the child in the parent's 'components' option.
I created an example scenario with simple Parent and Child component definitions in order to show a standard relationship implementation. Built with Vue 2 and the Vue CLI.
MySlider.vue (parent)
<template>
<div class="my-slider">
<h4>My Slider</h4>
<my-slider-item v-for="(item, index) in sliderItems" :key="index" :sliderItem="item" />
</div>
</template>
<script>
import MySliderItem from './MySliderItem.vue'
export default {
components: {
MySliderItem
},
data() {
return {
sliderItems: [
{
name: 'Slider Item 1'
},
{
name: 'Slider Item 2'
},
{
name: 'Slider Item 3'
}
]
}
}
}
</script>
MySliderItem.vue (child)
<template>
<div class="my-slider-item">
<h5>{{ sliderItem.name }}</h5>
</div>
</template>
<script>
export default {
props: {
sliderItem: {
type: Object,
required: true
}
}
}
</script>

vue wrap another component, passing props and events

How can I write my component to wrap another vue component, while my wrapper component get some extra props? My wrapper template component should be:
<wrapper-component>
<v-table></v-table> <!-- pass to v-table all the props beside prop1 and prop2 -->
</wrapper-component>
and the wrapper props:
props: {
prop1: String,
prop2: String
}
Here I want to wrap a table component, and pass to the table component all the props and events that were passed to the wrapper, beside two extra props prop1 and prop2. What is the correct way of doing this in vue?
And is there a solution for events too?
Place the component you wish to wrap into the template of the wrapper component, add v-bind="$attrs" v-on="$listeners" to that component tag, then add the inner component (and, optionally, inheritAttrs: false) to the wrapper component's config object.
Vue's documentation doesn't seem to cover this in a guide or anything, but docs for $attrs, $listeners, and inheritAttrs can be found in Vue's API documentation. Also, a term that may help you when searching for this topic in the future is "Higher-Order Component" (HOC) - which is basically the same as your use of "wrapper component". (This term is how I originally found $attrs)
For example...
<!-- WrapperComponent.vue -->
<template>
<div class="wrapper-component">
<v-table v-bind="$attrs" v-on="$listeners"></v-table>
</div>
</template>
<script>
import Table from './BaseTable'
export default {
components: { 'v-table': Table },
inheritAttrs: false // optional
}
</script>
Edit: Alternatively, you may want to use dynamic components via the is attribute so you can pass in the component to be wrapped as a prop (closer to the higher-order component idea) instead of it always being the same inner component. For example:
<!-- WrapperComponent.vue -->
<template>
<div class="wrapper-component">
<component :is="wraps" v-bind="$attrs" v-on="$listeners"></component>
</div>
</template>
<script>
export default {
inheritAttrs: false, // optional
props: ['wraps']
}
</script>
Edit 2: The part of OP's original question that I missed was passing all props EXCEPT one or two. This is handled by explicitly defining the prop on the wrapper. To quote the documentation for $attrs:
Contains parent-scope attribute bindings (except for class and style) that are not recognized (and extracted) as props
For example, example1 is recognized and extracted as a prop in the snippet below, so it doesn't get included as part of the $attrs being passed down.
Vue.component('wrapper-component', {
template: `
<div class="wrapper-component">
<component :is="wraps" v-bind="$attrs" v-on="$listeners"></component>
</div>
`,
// NOTE: "example1" is explicitly defined on wrapper, not passed down to nested component via $attrs
props: ['wraps', 'example1']
})
Vue.component('posts', {
template: `
<div>
<div>Posts component</div>
<div v-text="example1"></div>
<div v-text="example2"></div>
<div v-text="example3"></div>
</div>
`,
props: ['example1', 'example2', 'example3'],
})
new Vue({
el: '#app',
template: `
<wrapper-component wraps="posts"
example1="example1"
example2="example2"
example3="example3"
></wrapper-component>
`,
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Using props to set different text on every page in vue.js

I am using laravel + vue. I want to do title of page in navbar, so when you are in index, in navbar is text index. When you are in settings, navbar says settings etc.
I think props is good for it, but when I use that, it works not good. Look here:
blade.php of index:
#extends('layout.app')
#section('content')
<index :msg="msg"></index>
#endsection
index.vue:
props: [
'msg'
],
Now navbar:
<template>
<nav class="navbar">
<a></a>
<p>{{msg}}</p>
</nav>
</template>
<script>
export default {
props: [
],
data() {
return {
}
},
}
</script>
and layout:
<body>
<div id="app">
<navbar></navbar>
#yield('content')
</div>
</body>
How I can change that {{msg}} paragraph in navbar when we are on different pages? My code doesn't work.
[Vue warn]: Property or method "msg" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
If you want to use a prop then you need to define it in the props object of your component. In your NavBar you are referencing to msg, but the props object in NavBar is empty. Define it and pass the prop in your layout.blade.php.
Or you could also define a computed property where you take a look at the current route and return a string fitting your business.
https://v2.vuejs.org/v2/guide/computed.html
If you want to share data between multiple components, then use a store (VUEX) as proposed :)

Vue.js 2.0: Apply vue component rendered using v-html, compile the markup

I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>

Categories

Resources