Vue.js Trying to use props in style - vue.js

I am trying to use some props in a component of mine that show different v-icons and sizes depending on the values passed to it.
However when i try use one of the props to set the icon size with styles the icon doesnt change at all
Heres the code:
<template>
<div v-if="this.name === 'someName'">
<v-icon :style="style" >some-icon-name</v-icon>
</div>
<div v-else>
<v-icon :style="style" >some-other-icon-name</v-icon>
</div>
</template>
export default {
computed: {
style () {
return 'size: ' + this.iconSize + ';'
}
},
props: {
iconSize: {
type: Number,
required: false
},
name : {
type: String,
required: false
},
},
data () {
return {
//
}
}
}
</script>
When using the component i simply use v-bind to pass the props:
<appIcons v-bind:iconSize="90" v-name="someName" />

You seem to be confusing two things. v-icon has a prop called size whereas style is a Vue mechanism for setting custom CSS styles. Either could be used in this case. You cannot set a style of size, that's meaningless as size is not a CSS property.
I think what you want is this:
<v-icon :size="iconSize" >some-icon-name</v-icon>
That's using the size prop of v-icon rather than a custom style.
You could in theory do it using a style if you set the font-size. e.g.
style () {
return 'font-size: ' + this.iconSize + 'px'
}
Or perhaps using an object instead:
style () {
return {
fontSize: this.iconSize + 'px'
}
}

Related

How to do conditional rendering on dynamic props

I'm trying to add conditional rendering on the dynamically passed prop. I have a component called card-item.vue which is passing the prop cta to component profile.vue.
Now in profile.vue I want the prop cta to display on every card-item component except the first one.
Here is my card-item.vue:
<template>
<span v-if="cta">
{{ cta }}
</span>
</template>
<script>
export default {
name: 'card-item',
props: {
cta: {
type: String
}
}
}
</script>
profile.vue:
<template>
<CardItem
v-for="address in addresses.slice(1)"
:key="uniqueKey('address', address)"
:cta="cms.page.cta" // remove cta from the first address only
/>
</template>
<script>
import CardItem from "./card-item";
const data = require('./profile.json');
export default {
name: 'profile',
comonents: {
CardItem,
},
props: {
cms: {
type: Object,
default: () => {
return {
page: data
}
}
}
}
}
</script>
profile.json:
{
"cta": "Report"
}
In my <CardItem /> component I'm rendering addresses. So I want my :cta on every address except the first one.
I was trying something like:
v-if="addresses[0] ? {cta="null"} : "cms.page.cta""
I know this is incorrect syntax but somewhat I'm trying to achieve.
Please any help would be appreciated.
v-for also supports an optional second argument for the index of the
current item. -- vue docs
<CardItem
v-for="(address, index) in addresses.slice(1)"
:key="uniqueKey('address', address)"
:cta="index !== 0 ? cms.page.cta : null" // remove cta from the first address only
/>

How can I can show a div if an object doesn't have a value outside of the object scope

I have a small Nuxt issue that I can't work out how to get around.
Essentially, I have an object (used for a carousel slider).
<template>
<div
:class="[$style.swiperSlide, 'swiper-slide']"
v-for="slide in slides"
:key="slide.id">
<nuxt-img
:class="[$style.img]"
:alt="slide.alt"
:src="imgSources(slide)"
sizes="sm:100vw"
/>
<div :class="[$style.info, 'info-b']" v-if="slide.info">
{{ slide.info }}
</div>
</div>
<button :class="[$style.infoOpen]"
#click="showTab"
v-if="slideInfoAvailable"
>
Close
</button>
</template>
<script>
export default {
props: {
slides: {
type: Array,
required: true,
default: () => []
}
},
computed: {
slideInfoAvailable() {
return this.slide?.info
}
},
mounted() {
const swiper = new Swiper(".swiper-container", {
. . .
});
},
methods: {
imgSources(slide) {
return `/img${slide.imgPath}.jpg`;
},
};
</script>
All works o.k, the problem is that I have a button outside of this v-for that I need to only be visible if there's slide.info but because this div is outside of the v-for it can't tell if it's available.
Cannot read property 'info' of undefined
The easiest way out of this is to add the button inside of the slider - but I can't for Z-index CSS issues. It has to be outside of the 'slider' div.
Any ideas how I can only show the button if there's slide.info? For some of my slides, there won't be.
<slider
:slides="[
{
imgPath: '/demo',
info: 'Demo info for this slide',
alt: 'Homepage'
},
{
imgPath: '/demo2',
alt: 'Homepage'
},
]"
/>
One way I could do it would be to see if .slide-active .style.info exists. If it doesn't exist then I can hide the button as slide-active is added to the active div by the slider API.
The issue is coming from the fact that you probably have some async fetching and that slides are not available upon initial render. To prevent this, you can use a computed with some optional chaining like this
export default {
computed: {
slideInfoAvailable() {
return this.slide?.info
}
}
}
Then, call it like this
<button :class="[$style.infoOpen]" #click="showTab" v-if="slideInfoAvailable">
You cannot use ?. directly in the template.
You could also do the classic way of
<button :class="[$style.infoOpen]" #click="showTab" v-if="slide && slide.info">
but it does not look as sexy IMO (but you do not need any computed).
And yeah, for this kind of thing, better to handle it with Vue than relying on some hacky dirty CSS tricks!

Vue Mixin - Handle Data Properties

I am trying to use mixins for some reusable methods being used in several of my Vue SFCs.
Now, what I am worried about is how does mixins handle data properties? I find that it is too easy to make errors when using mixins (e.g. when renaming the data properties in the main component and it is being accessed in the mixin). What is the best way to handle such scenario?
// main.vue
<template>
<div>
Input Name:
<input v-model="mainComponentValue" placeholder="edit me">
<br/><br/>
{{ welcomeMessage }} <br/>
Member ID: {{ getMemberIdNum() }}
<br/><br/>
{{ checkMemberOftheMonth() }}
<br/><br/><br/>
{{ someMainComponentMethod() }}
</div>
</template>
<script>
import mixin from ‘./mixin.js’
export default {
mixins: [mixin],
data () {
return {
title: 'Component',
mainComponentValue: 'John'
}
},
methods: {
someMainComponentMethod () {
return 'This is a computed property from the mixin: ' + this.memberName
}
}
}
</script>
Then on my mixin:
// mixin.js
export default {
data () {
return {
mixinSampleValue: 'Hello there'
}
},
computed: {
welcomeMessage () {
return this.mixinSampleValue + ', ' + this.mainComponentValue.toUpperCase() + '!'
},
memberName () {
return this.mainComponentValue.toLowerCase()
}
},
methods: {
getMemberIdNum () {
switch (this.memberName) {
case 'john':
return '0001'
case 'roger':
return '0002'
default:
return '0003'
}
},
checkMemberOftheMonth () {
// use main component's data
if (this.memberName === 'john') {
return 'Congratulations, you are our member of the month!'
}
}
}
}
As you can see, main.vue data properties are used in the mixin and vice versa (and computed properties and methods).. So I worry that if I change the data property name (let's say mainComponentValue to name) at main.vue or any other vue component that uses the mixin, then it will mess my mixin up. And vice versa when I change a computed property from the mixin, then main.vue will be messed up.
Any way I can add certain checkings for cases like this?
For reference, here is a sample CodePen:
https://codepen.io/keechan/pen/eYBRQGM
Thanks!
We do not use mixins anymore (almost). We had the same issues in our dev team, that it is not obvious inside a component what the mixin provides.
Instead we use full or renderless components with scoped slots or we simply import some functions from a lib directory that we want to share across several vue components.
Another alternative is the Composition API, see also: https://css-tricks.com/how-the-vue-composition-api-replaces-vue-mixins/

vue property as prop name without value

I want to pass a property that doesn't have any value - only name. I want the name to be dynamic according the prop.
I want to create a component of tool-tip that get the tool-tip side -> top left right or bottom. in v-tooltip there isn't property side, all side is another property without value. I want it to change according the prop (side is a variable - a prop of my component).
<template>
<v-tooltip side>
<template slot="activator">
<slot slot="activator"></slot>
</template>
<span>{{text}}</span>
</v-tooltip>
</template>
<script>
export default {
props: {
text: {
type: String,
required: true,
},
side: {
default: 'top',
},
},
}
</script>
I can't find a way to use prop as the name of the property
There are two things that you need to realise. In Vue, calling a component with <my-component has-this-prop /> it the same as calling a component with <my-component :has-this-prop="true" />. The boolean basically toggles the prop.
You can also dynamically change which props are bound to a component by using the v-bind syntax without a prop name, as outlined in the documentation.
This means that if you have a prop called side in your parent component that always contains the correct side, you can create a computed property that contains all the props you want to pass, then use v-bind on your v-tooltip to pass the props:
<template>
<v-tooltip v-bind="tooltipProps">
<!-- Something -->
</v-tooltip>
</template>
<script>
const validSides = [
'top',
'top left',
'top center',
'top right',
// you get the idea
];
export default {
props: {
side: {
type: String,
default: 'top',
validator(prop) {
return validSides.contains(prop);
}
}
},
computed: {
tooltipProps() {
if (!validSides.contains(this.side)) {
return {};
}
const propsNames = this.side.split(' ');
const props = {};
for (const propName of propsNames) {
props[propName] = true;
}
return props;
}
}
}
</script>

VUE2js: How to have component re-render after its props change?

a Vue newbie here. The thing is is simple:
<template>
<btn :color="color" #click="toggleColor">{{btnmsg}}</btn>
</template>
<script>
import { Btn } from 'chico'
export default = {
name: 'AButton',
componenents: {
Btn
},
data () {
return {
btnmsg: 'Legia pany'
colors: ['blue', 'black', 'green', 'organge', 'teal', 'cyan', 'yellow', 'white'],
color: 'red'
}
},
methods: {
toggleColor () {
this.color = this.colors[Math.floor(Math.random() * Math.floor(this.colors.length))]
}
}
</script>
The 'Btn' from the ChicoFamily goes something like this
<template>
<button :is="tag" :class="[className, {'active': active}]" :type="type" :role="role" ">
<slot></slot>
</button>
</template>
<script>
import classNames from 'classnames';
export default {
props: {
tag: {
type: String,
default: "button"
},
color: {
type: String,
default: "default"
...it takes hellotta props...
},
data () {
return {
className: classNames(
this.floating ? 'btn-floating' : 'btn',
this.outline ? 'btn-outline-' + this.outline : this.flat ? 'btn-flat' : this.transparent ? '' : 'btn-' + this.color,
...classes derived from these props...
)
};
}
};
</script>
Yes, it is a button that, when clicked, should change its color. Clicking it indeed changes a prop passed, but does not, in fact, have the button re-rendered. I am asking this question, because I feel like there is something bigger about Vue2 mechanics that is eluding me.
Why passing a different prop does not re-render this sweet-baby-to-be button? How does one do it properly?
Best, Paco
[edit:] The Btn takes its color from Bootstrap classes deriving from the prop. Can it be that it gets the proper props in, but the className mechanic does not catch up?
Setting a :key on the component is the best way to force Vue to re-render the component. If you require the component to be re-rendered, simply modify the value of the key, and Vue will re-render it.
Your color isn't reactive because you set it as a data and not as a computed.
The way you did it, the className will be set once when the instance will be created.
In ordre to make the className re-evaluate each time you change one of the props in state, you will have to make a computed property out of this :
Btn component :
export default {
props: {
[...]
},
computed: {
className() {
return classNames(
this.floating ? 'btn-floating' : 'btn',
this.outline ? 'btn-outline-' + this.outline : this.flat ? 'btn-flat' : this.transparent ? '' : 'btn-' + this.color);
);
},
},
}