vue.js component inline style concatenation - javascript

I'm stuck with a vue.js component inline style concatenation.
My code is the following:
components: {
'twitter-item': {
props: ['procolors'],
template: '\
<div class="color-quadrat" v-bind:data-id="procolors.id" v-bind:style="background-color: #{procolors.user.profile_background_color}">\
<p>{{procolors.user.profile_background_color}}</p>\
</div>\
'
}
}
I'm trying to get procolors.user.profile_background_color as inline background color. Special is that the value from procolors.user.profile_background_color has no #. So I have to add this in the template.
I tried all kinds of recommendations from the web, but none worked for me.
Any help appreciated!

Use this, which utilizes vue's style object syntax:
:style="{backgroundColor: '#' + procolors.user.profile_background_color}"

You have several choices in how to add styling. If you use v-bind:style="...", or it shorthand :style="...", you need to pass either a valid string, valid variable or a valid object.
Currently you are trying to parse background-color: #{procolors.user.profile_background_color} as javascript, which is not going to work.
You can use a javascript template to create a string:
components: {
'twitter-item': {
props: ['procolors'],
template: '\
<div class="color-quadrat" v-bind:data-id="procolors.id" v-bind:style="`background-color: #${procolors.user.profile_background_color}`">\
<p>{{procolors.user.profile_background_color}}</p>\
</div>\
'
}
}
It is often more readable to refactor it to use a variable or function instead:
components: {
'twitter-item': {
props: ['procolors'],
template: '\
<div class="color-quadrat" v-bind:data-id="procolors.id" v-bind:style="rowColor">\
<p>{{procolors.user.profile_background_color}}</p>\
</div>\
',
computed: {
rowColor () {
return {
"background-color": `#${this.procolors.user.profile_background_color}`
}
}
}
}
}

Accoding to Binding inline styles documentation there are to ways to pass inline styles - as an object or as an array.
In your example, background-color: #{procolors.user.profile_background_color} is neither object or an array.
For sake of readability and maintainability (and good practice in general), I'd suggest to create a computed property that will return an object with inline styles. This way it will more clear where is the issue with concatenation:
Template will look as follows:
<div
class="color-quadrat"
:data-id="procolors.id"
:style="itemStyles">
<p>{{ procolors.user.profile_background_color }}</p>
</div>
And computed property should be added to the same component:
props: ['procolors'],
template: `...`,
computed: {
itemStyles () {
return {
backgroundColor: `#${this.procolors.user.profile_background_color}`
}
}
}
If you still prefer to keep it inline, then style binding should be changed to following:
v-bind:style="{ backgroundColor: `#${procolors.user.profile_background_color}` }"

For those who want to use style binding with vue3. This is the solution:
<script setup lang="ts">
import {ref} from 'vue'
const color = ref("red")
</script>
<template>
<div class="parti-color"
:style="{backgroundColor: color, width: '20px', height: '30px'}"
/>
</template>

Related

Vue component not being rendered with props [duplicate]

This question already has an answer here:
Vue basics -- use component in other component
(1 answer)
Closed 2 years ago.
I am beginner in Vue. I created a custom component and tried to bind everything exactly as shown in the starter Vue cli template. Here is the code.
Circle.vue
<template>
<div :style="custom">
</div>
</template>
<script>
export default {
name:'Circle',
props:{
size:String,
color:String
},
computed:{
custom(){
return {
background:this.color,
height:this.size,
width:this.size
}
}
}
}
</script>
inside my View.vue file
<script>
// :class="['']"
import Circle from '#/components/Circle.vue'
export default {
name: "Landing",
components:{
Circle
}
};
</script>
I tried to use it so
<Circle size="100px" color="#222222"/>
I tried printing the props as it is but it also doesnt work
<template>
<div :style="custom">
{{size}} {{color}}
</div>
</template>
Nothing is being shown on the screen after I do this. I took help from here
Thanks for your time!
As mentioned in the docs:
Component names should always be multi-word, except for root App components, and built-in components provided by Vue, such as <transition> or <component>.
This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.
You have two options when defining component names:
With kebab-case
Vue.component('my-circle', { /* ... */ })
When defining a component with kebab-case, you must also use kebab-case when referencing its custom element, such as in <my-circle>.
With PascalCase
Vue.component('MyCircle', { /* ... */ })
When defining a component with PascalCase, you can use either case when referencing its custom element. That means both <my-circle> and <MyCircle> are acceptable.
Demo:
Vue.component('my-circle', {
props: {
size: String,
color: String
},
template: '<div :style="custom"></div>',
computed: {
custom() {
return {
background: this.color,
height: this.size,
width: this.size
}
}
}
})
new Vue({
el: "#myApp"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
<my-circle size="100px" color="#222222" />
</div>

'v-bind' directives require an attribute value

I am trying to create some type of tree with vue.js and stuck on a problem with element props. Help me out plz.
I've tried :content="{{tempCont}}" and I've tried content="{{tempCont}}", but none of them worked.
Here's the place where I am using tree element:
<div id="tree">
<treeItem :title="Parent" :content="{{tempCont}}"></treeItem>
</div>
Here's the entire tree element:
<template>
<div>
<p v-on:click="openTree">{{title}}</p>
<div id="childs" v-if="childVisibility">
<treeItem v-for="item in content" :key="item" title=item>
</div>
</div>
</template>
<script>
export default {
data: {
childVisibility: false
},
methods: {
openTree: function(){
childVisibility = !childVisibility;
}
},
props: {
title: String,
content: Array,
}
}
</script>
<style scoped>
</style>
I am getting this error:
Use like this: :content="tempCont"
<div id="tree">
<treeItem :title="Parent" :content="tempCont"></treeItem>
</div>
Ok so first of all, when you v-bind something like v-bind:title or :title, what you bind is expressed as a javascript expression.
So if you want your title attribute to be the string Parent, you need either to write it like a native html attribute title="Parent" (notice the lack of :), or as a vue bound attribute v-bind:title="'Parent'" or :title="'Parent'" (notice the use of '' to express a string primitive type in javascript.
Now, the {{ variable }} syntax is used inside vuejs template but you do not need to use it inside v-bind attributes since they are already interpreted as javascript.
So you shouldn't write this:
<div id="tree">
<treeItem :title="Parent" :content="{{tempCont}}"></treeItem>
</div>
but this instead:
<div id="tree">
<treeItem title="Parent" :content="tempCont"></treeItem>
</div>
SincetempCont is already a valid javascript expression.
You don't really need {{}} for passing attributes.
<treeItem :title="Parent" :content="tempCont"></treeItem>
This shall be good enough to work. The puspose of {{}} is to print data and not pass attributes.
Also, in your tree component, it's a good practice to follow object notations in your props. For ex:
props: {
title: {
type: String
},
content: {
type: Array
},
}
Also you should make your components data reactive and making sure that childVisibility is set to this instance rather than a direct reference by setting it like this
export default {
data() {
return {
childVisibility: false
}
},
methods: {
openTree() {
this.childVisibility = !this.childVisibility;
}
},
props: {
title: String,
content: Array,
}
}

Use style tags inside vuejs template and update from data model

I would like to dynamically update styles inside style tags.
However creating a container model for Vue, removes the style tags.
I know style tags should belong in the head of the page, but this is just for the sake of ease of use.
So what I would like to have is a wrapper with an element and style tags inside:
<div class="setting">
<style>
.setting input {
background: {{bgColor}};
}
</style>
<input class="setting" type="text" v-model="bgColor">
</div>
The value from the input should update the value of the css style.
Whenever done with simple div elements this works, but style tags seem to be a problem
The javascript set up is the following:
new Vue({
el: '.setting',
data: {
bgColor: 'red'
}
});
However when the style tags have a specific id, this could work, but I can't bind it to an input field.
<style id="setting">
#blue {
background: {{bg}}
}
#blue:hover {
background: {{bgHover}}
}
</style>
<div id="blue"></div>
and the js:
new Vue({
el: '#setting',
data: {
bg: 'blue',
bgHover: 'red'
}
});
Can someone help me understand how I can achieve updating values between style tags.
jsfiddle set up
Thanks.
Here's what I think is a good workaround/solution.
It is just a custom component, so it's as reusable as it gets. All of Vue's goods like v-if can all be used.
Another pro is that the styles generated will be there only as long as the component is!
Vue.component('v-style', {
render: function (createElement) {
return createElement('style', this.$slots.default)
}
});
// demo usage, check the template
new Vue({
el: '#app',
data: {
bgColor: 'red'
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app" class="stuff">
<v-style>
.stuff input {
background: {{bgColor}};
}
</v-style>
Remove "red" and type "yellow":
<input class="setting" type="text" v-model="bgColor">
</div>
The one drawback I see is that since the name of the tag is <v-style> (or whatever you chose to call it) and not <style>, the IDEs may not color it nicely. But otherwise it'll just be like a regular <style> tag.
Standard solution: using v-bind:style
This doesn't modify style tags, but the standard way of setting styles is using object style bindings.
Basically you'd use a :style attribute and assign to it the CSS properties of the style in the form of an object. Demo below.
new Vue({
el: '.setting',
data: {
bgColor: 'red'
},
computed: {
inputStyles() {
return {
background: this.bgColor
}
}
}
});
<script src="https://unpkg.com/vue"></script>
<div class="setting">
Remove "red" and type "yellow":
<input class="setting" type="text" v-model="bgColor" :style="inputStyles">
</div>
vue-loader (and Vue template compiler Vue.compile(..)) both will filter out any <style> tags that are encountered in the template.
A simple solution to get around this, is to take advantage of Vue's built-in<component> component.
<template>
<div>
<component is="style">
.foo[data-id="{{ uniqueId }}"] {
color: {{ color }};
}
.foo[data-id="{{ uniqueId }}"] .bar {
text-align: {{ align }}
}
</component>
<div class="foo" :id="id" :data-id="uniqueId">
<div class="bar">
</div>
</div>
</div>
</template>
<script>
export default {
props: {
id: {
type: String,
default: null
}
},
computed: {
uniqueId() {
// Note: this._uid is not considered SSR safe though, so you
// may want to use some other ID/UUID generator that will
// generate the same ID server side and client side. Or just
// ensure you always provide a unique ID to the `id` prop
return this.id || this._uid;
},
color() {
return someCondition ? 'red' : '#000';
},
align() {
return someCondition ? 'left' : 'right';
}
}
}
</script>
A unique ID (or some other data-attribute) is required to "scope" the styles to just this component.
This is a nice solution as you can use v-for loops to generate the style content if required (which can be reactive to changes in your components data/props/computed props)
<component is="style" type="text/css">
<template v-for="item in items">
[data-id="{{ uniqueId }}"] div.bar[data-div-id="item.id"]::before {
content: "{{ item.content }}";
color: {{ item.color }};
}
</template>
</component>
I just created a computed property and used it in the template as v-html
<div v-html="css"></div>
computed: {
css () {
return `<style>${this.item.css}</style>`
}
}

Including libraries in components through computed properties

When I try to call libraries such as lodash or momentjs inside the {{ mustache syntax }} in Vue template files I keep getting the 'undefined property' error, even though they are bound to the window object.
To go around this, I am importing the libraries into my components, and then returning the objects in computed properties, as in:
import _ from 'lodash';
export default {
computed: {
_() { return _; }
}
}
Is it a good way to handle this issue? What would be the recommended approach? Are there any downsides to handling it through computed properties instead of methods?
Basically, yes, creating a computed property is the way to go.
But you have some alternatives to make that simpler.
A Global Mixin is one good alternative that requires little code:
Vue.mixin({
computed: {
window() { return window; }
}
})
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ window.location.host }}</p>
</div>
This way the window computed property will be available to (and thus usable in the template of) any Vue component.
I would recommend to move all logic to compute property. This makes your code cleaner and more readable.
Think of your component as MVC, where templates are View, the component object is a controller and properties and computed properties are model. You don't put logic in the view layer, right?
So instead of
<template>
<div>
{{ _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x') }}
</div>
</template>
export default {
computed: {
_() { return _; }
}
}
something like this:
<template>
<div>
{{ diff1 }}
</div>
</template>
export default {
computed: {
diff1: _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'),
}
}

Filter with VueJS

I am very new with VueJS so my question is very simple. I cannot use vue filter. Please help me fix the problem.
My html file is shown as followed. When I try this code the item in v-for can't be shown and also the it has error Failed to resolve filter: uppercase.
Can any one tell me why?
<div id="pan" class="pan">
<div v-for="item in list|orderBy 'level'" >{{item.id}}</div>
<span>{{message | uppercase}}</span>
</div>
<script type="text/javascript" src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
var pan = new Vue({
el: '#pan',
data: {
list: [
{ name: '東京', id:"TOKYO",level:"2"},
{ name: '全国',id:"JAPAN",level:"1" },
{ name: '関東',id:"KANTO",level:"0" },
],
message:"hello"
}
});
</script>
If you are using vuejs2, with vuejs2 uppercase filter has been removed. You will have to use toUpperCase() for this, like following:
<span>{{message.toUpperCase()}}</span>
see demo.
Similarly orderBy filter also has been removed, vuejs2 suggests to use lodash’s orderBy (or possibly sortBy) in a computed property:
HTML
<p v-for="item in orderedList">{{ item.name }}</p>
vue
computed: {
orderedList: function () {
return _.orderBy(this.list, 'level')
}
}
Here is demo with orderBy.
You can use a computed property.
Markup:
<div id="pan" class="pan">
<div v-for="item in orderedList" >{{ item.id }}</div>
<span class="pan__title">{{ message }}</span>
</div>
Definition inside of Vue:
data(){
sortKey : 'level'
},
computed : {
orderedList(){ return this.list.sort(this.sorter) }
},
methods : {
sorter(a,b){ return a[this.sortKey] > b[this.sortKey] }
}
And then you can change order of the elements in orderedList by modifying sortKey (using v-model="sortKey" to any kind of input, like <select></select> or any other way).
Here is an example based on your code
And what about uppercase, I prefer to control a view with css, and text-transform property can solve this: .pan__title { text-transform: uppercase; }. But you can define a computed property for this one too or keep it inline with {{ message.toUpperCase() }}.

Categories