v-bind:style syntax not working in inline styles - vue.js

I'm fairly knew to Vue and the whole v-bind thing is throwing me for a loop...
Basically, I'm trying to achieve this syntax, but with Vue's v-bind, since I can't use a variable in an inline CSS style:
<div class="card" style="background-color: {{school.color}}">
Here's my Vue syntax, which I've followed from their documentation, but it's still throwing an error and I can't figure out why? Obviously, it has to be something simple, I just don't fully understand the intricacies of Vue yet!
<div class="card" :style="{ background-color: school.color }">
Any help would be much appreciated!

For object syntax bindings you are binding an object. Thus, the keys must be valid, and need to be quoted unless they are valid unquoted keys. Dashes - are not allowed in unquoted keys, so it fails to compile.
Either of these options will work:
<div class="card" :style="{ 'background-color': school.color }">
or
<div class="card" :style="{ backgroundColor: school.color }">

Related

Require is not a function in vue-cli project while rendering using template attribute

I'm new to Vue js (I'm using vue3) and I've detected a bit weird behavior at my Component while image rendering
I have a project with 2 endpoints Home.vue and InitializationModal.vue
The Problem is, that while rendering the InitializationModal.vue component, the vue does not determine the require as a function for some reason (I put the template into the template attribute of the component, not into the separated tag)
I have the same template at Home.vue (but it is in separated template tag), however it works perfect.
What's wrong with it?
To better explain the problem, there is some snippets provided down below
I've cut it as much as it possible to make it easier to understand
Snippet of the Home.vue (that is in separated tag)
<template>
<div v-else class="empty flex flex-column">
<img :src="require('#/assets/illustration-empty.svg')" style="width: 50%; height: 50%; margin" alt="illustration-empty" />
<h3>There is nothing here</h3>
<p>Let's create a new Virtual Server Now!</p>
</div>
</div>
</template>
<script>
...// my component goes there
</script>
InitializationModal.vue
<script>
export default {
name: "hardwareConfiguration",
template: `
<div class="hardwareConfiguration flex flex-column">
<h4>Hardware Configuration</h4>
<v-select :options="Datacenters" #input="validateDatacenter" label="title">
<template slot="Datacenter" slot-scope="Datacenter">
<img :src="require('#/assets/some_image.svg')" style="height: 20%; width: 20%" />
{{ Datacenter.DatacenterName }}
</template>
</v-select>
`,
};
</script>
the Error that is being returned at the InitializationModal.vue : Uncaught (in promise) TypeError: require is not a function
Using require( like this is not really Vue, it's Webpack which reads your require method, transforms the path to an absolute path which the browser understands (or possibly a base64 encoded string, depending on url-loader settings). The problem is webpack will only transform Javascript, not the full string of your template property. Maybe try like this:
`<img :src="${require('#/assets/some_image.svg')}" style="height: 20%; width: 20%" />`
or just use regular SFC template syntax.

How to use background-image in inline styles in Nuxt.js

I have a .vue file where I want to use inline styles
<img src="~static/img/info/production/pic4.jpg" class="t22" />
First text
<div class="hr t24"></div>
<h2 class="t25">Second text</h2>
<ul class="services">
<li :style="{ backgroundImage: `url('~static/img/info/production/pic5.jpg')` }" class="sq">
<div class="n">Third text</div>
</li>
</ul>
The image using tag <img> is displayed correctly, but background-image in tag <li> is not.
How do I specify the file path correctly?
EDIT: really did not expect to have to host this one but here it is: https://codesandbox.io/s/so-nuxt-background-image-bf0ly?file=/pages/index.vue
Exact structure, same (ESlinted) template, working solution. Cannot help beyond this point.
I'm not sure that it is the universal solution, but this one is working on my side
<li :style="{ backgroundImage: `url(${require('#/static/japan.jpg')})` }">
The require seems to be required here, not sure why but it works. More in the docs: https://nuxtjs.org/docs/2.x/directory-structure/assets/#images

How to replace css class name partially in Vue?

Let's say I have this html element
<div class="progress-bar w-90"></div>
and I want to replace 90 with an object from vue. I tried below but syntax
<div :class="progress-bar w-{{ progress.value }}"></div>
You cannot use {{ }} in attribute/props, better to use template literals as below.
<div :class="`progress-bar w-${progress.value}`"></div>
You can achieve text-binding in this way
<div :class="'progress-bar w-'+progress.value"></div>

Style Binding not updating with template structure [Vue]

In my example I want to draw a border around an element after clicking on it. In this example it works perfectly:
<div v-for="(parent, index) in $store.getters.getInfo" :key="index">
<div #click="setClicked" :id="child.id" v-for="child in parent"
:style="[child.clicked ? {'border-color': 'black'} : {'border-color': 'white'}]">
</div>
</div>
But when i try with a bit more complicated structure and template tags the style binding fails to be triggered:
<div v-for="i in 12">
<template v-for="(user, index) in $store.getters.getSharedUsers">
<div :id="child.id" v-for="child in $store.getters.getSharedInfo[user[0]][i-1]"
:userID="child.userID" #click="setClicked"
:style="[child.clicked ? {'border-color': 'black'} : {'border-color': 'white'}]">
</div>
</template>
</div>
In my mutation I set the attribute with:
Vue.set(state.element_map[payload.uID][payload.dID], 'clicked', true);
When I debug I see the change in both code examples in my data structure after calling the setClicked function, but only in the first one the style binding is triggered and the border is drawn. The only difference I see is the use of the template tag and the more complicated data structure. But it should also work in the second example as the clicked attribute is set correctly. So what am I missing? Thanks!

Why does my Vue component require :key?

I have a small Vue.js component which displays a favorite star icon. Clicking on the icon favorites/unfavorites the element. So far I have only implemented the UI part, which looks like this:
<template>
<div :key="favorite">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="favorite" class="text-warning fas fa-star"></i>
<i v-show="!favorite" class="text-warning far fa-star"></i>
</a>
</div>
</template>
<script>
export default {
data() {
return {
favorite: true,
}
},
mounted() {
},
methods: {
toggleFavorite() {
this.favorite = !this.favorite
}
},
props: ['team-id'],
}
</script>
<style scoped>
</style>
As you can see, the logic is pretty simple.
This works well, but one thing that bothers me is that, if I remove the :key property from my template, the icon is not updated when I click on it (even though I have checked that the underlying property is indeed updated correctly). Adding :key makes it work, I imagine because it forces Vue.js to completely re-render the component when favorite is updated.
Why is this happening? I'm fairly new to the world of JS frameworks, so forgive any obvious stuff I might be missing. I did some research online but couldn't find an explanation. I just want to make sure I'm doing things the right way and not merely hacking around the issue here.
Vue patches with the virtual DOM whenever it is necessary. That is, whenever vue detects the changes on the DOM, it patches them for faster performance. And patching in the DOM will not change the icon or image. You need to replace the DOM instead.
Thus, vue provides the way for us whenever we need to change the DOM by replacing method, we can use :key binding.
So, :key binding can be used to force replacement of an element/component instead of reusing it.
The following whole html div will be replaced whenever there is change in favorite data as we're :key binding on it:
<div :key="favorite">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="favorite" class="text-warning fas fa-star"></i>
<i v-show="!favorite" class="text-warning far fa-star"></i>
</a>
</div>
This is why vue forcefully allows us to use :key binding inside a loop as there's need of replacing the elements inside the loop whenever it detects the changes in the data. This is made compulsory from 2.2.0+ and ESLint also have implemented this feature so that if you miss :key binding inside the loop, then you'll see the error on that line when you use editor that supports eslint, so that you can fix the error.
Just an opinion, the strict requirement of the :key binding should be removed from the vue as we might want a loop of predefined data and don't want to change the DOM but we still use the v-for loop for listing bigger data. But it might be rare case though.
Read carefully on the documentation for :key binding and then you'll have an idea.
The :key binding can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
Use :key binding to replace the DOM. Remember it slower the performance as it replace the whole DOM that is bound to the element.
Don't use :key binding when you don't want to replace the DOM or
you think there's no data changes detection required. This will
allow vue to perform better without :key binding.
Its seems to be a general issue of FontAwesome CSS regardless the framework.
There is an issue on github and here the same issue with react https://github.com/FortAwesome/Font-Awesome/issues/11967
To prove that, here is a simplified version of the same example but using bootstrap icons
new Vue({
el: '#app',
data() {
return {
fav: true
}
}
});
<script
src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"
></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<div id="app">
<div>
<a v-on:click="fav = !fav" style="cursor: pointer">
<i v-show="fav" class="glyphicon glyphicon-star"></i>
<i v-show="!fav" class="glyphicon glyphicon-star-empty"></i>
</a>
</div>
</div>
You shouldn't need the :key, it's only necessary in v-for loops. I would suggest you remove it and replace your v-show with a v-if and v-else directive.
<i v-if="favorite" class="text-warning fas fa-star"></i>
<i v-else class="text-warning far fa-star"></i>
v-if removes and addes the section to the DOM whereas v-show just hides it so this way well resolve your issue
Ok I think the problem here is that you're changing your root data object. To preserve reactivity, you shouldn't change the root data object after you've instantiated Vue.
Here is your code in a simple Vue. I didn't need :key to make it work. I would keep :key for inside loops.
markup
<div id="vueRoot">
<a v-on:click="toggleFavorite" style="cursor: pointer">
<i v-show="store.favorite" class="text-warning fas fa-star">Fav</i>
<i v-show="!store.favorite" class="text-warning far fa-star">Not fav</i>
</a>
</div>
code
vm = new Vue({
el : "#vueRoot",
data() {
return { store :{
favorite: true
}}
},
mounted() {
},
methods: {
toggleFavorite() {
this.store.favorite = !this.store.favorite
}
}
}
);
This is a working example with minimal changes. From what you've showed us, you should just have <i> element, then do what you want with a dynamic class list, like...
<i :class="['text-warning','fa-star',store.favorite?'fas':'far']"></i>