How to pass a style property to a child component as a computed property in Vue.js? - javascript

I have the following problem:
I have too much logic in my inline style and would to place it inside a computed property. I know, that this is the way, that I should go, but do not know, how to achieve it.
Below I a simple example that I made for better understanding. In it, on button press, the child's component background-color is changing.
My code can be found here: Codesandbox
My parent component:
<template> <div id="app">
<MyChild :colorChange="active ? 'blue' : 'grey'" />
<p>Parent:</p>
<button #click="active = !active">Click Me!</button> </div> </template>
<script> import MyChild from "./components/MyChild";
export default { name: "App", components: {
MyChild, }, data() {
return {
active: false,
}; }, }; </script>
and my child component:
<template> <div class="hello">
<p>Hello from the child component</p>
<div class="myDiv" :style="{ background: colorChange }">
here is my color, that I change
</div> </div> </template>
<script> export default { name: "HelloWorld", props: {
colorChange: {
type: String,
default: "green",
}, }, }; </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .myDiv { color: white; padding: 1rem 0; } </style>
I also have a second question. Let's say, that I have more than one child component and also would like to change to colors on button click, but those colors differ. How can I achieve it without repeating myself (within the computed properties?)
Code example for my parent component:
<MyChild :colorChange="active ? 'blue' : 'grey'" />
<MyChild :colorChange="active ? 'grey' : 'blue'" />
<MyChild :colorChange="active ? 'blue' : 'red'" />
<MyChild :colorChange="active ? 'yellow' : 'blue'" />
Thanks in advance!

Maybe You can bind class and use different css classes:
Vue.component('MyChild',{
template: `
<div class="hello">
<p>Hello from the child component</p>
<div class="myDiv" :class="collorCurrent">
here is my color, that I change
</div>
</div>
`,
props: {
colorChange: {
type: String,
default: "green",
},
colorDef: {
type: String,
default: "green",
},
isActive: {
type: Boolean,
default: false,
},
},
computed: {
collorCurrent() {
return this.isActive ? this.colorChange : this.colorDef
}
}
})
new Vue({
el: "#demo",
data() {
return {
active: false,
}
},
})
.myDiv { color: white; padding: 1rem; }
.blue {
background: blue;
font-size: 22px;
}
.red {
background: red;
font-variant: small-caps;
}
.yellow {
background: yellow;
color: black;
}
.grey {
background: grey;
text-decoration: underline;
}
.green {
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<p>Parent:</p>
<button #click="active = !active">Click Me!</button>
<my-child :is-active="active" :color-def="'grey'" :color-change="'blue'"></my-child>
<my-child :is-active="active" :color-def="'blue'" :color-change="'grey'"></my-child>
<my-child :is-active="active" :color-def="'red'" :color-change="'blue'"></my-child>
<my-child :is-active="active" :color-def="'blue'" :color-change="'yellow'"></my-child>
</div>

Related

Update elements background color in VueJs

In my app, I have 4 components:
App.vue
ProfileCardSlider
ProfileCard
ColorPicker
The idea is users would be able to update the background color of their ProfileCard using the element.
I can't work out how I can get the color value from the input and then emit that value up to the parent (App.vue in this case). In App.vue I believe I also need a color data property which this emitted value would then update and pass down as props to the ProfileCard.
These are my two components:
App.vue
import ProfileCardSlider from "./components/ProfileCardSlider.vue";
import ColorPicker from "./components/ColorPicker.vue";
export default {
components: {
ProfileCardSlider,
ColorPicker,
},
data() {
return {
color: "red",
};
},
methods: {
updateColor() {
this.color = color;
},
},
};
</script>
<template>
<ColorPicker :color="color" #select-color="updateColor" />
<ProfileCardSlider :color="color" />
</template>
ColorPicker.vue
<script>
export default {
props: ["color"],
methods: {
selectColor(color) {
this.$emit('update-color', color)
},
},
};
</script>
<template>
<div class="color__picker">
<h2 class="color__picker--title">Card background colour:</h2>
<input type="color" #change="selectColor" />
</div>
</template>
Using v-model and props in the correct way can help achieve this.
In your App.vue, use the color data property as hex (best practice to use in props), i.e #000000, and bind the color property to v-model to ColorPicker component.
Then in ColorPicker component, use the value prop which is the v-model binding from App.vue, and emit an input event on color input updating.
At last, listen to the color-changing event in App.vue to pass the updated color to ProfileCardSlider.vue.
So, the final code should be-
App.vue
<template>
<div>
<ColorPicker v-model="color" #input="updateColor" />
<ProfileCardSlider :color="color" />
</div>
</template>
<script>
import ProfileCardSlider from "./components/ProfileCardSlider.vue";
import ColorPicker from "./components/ColorPicker.vue";
export default {
name: "App",
data() {
return {
color: "#ffffff",
};
},
components: {
ProfileCardSlider,
ColorPicker,
},
methods: {
updateColor(color) {
this.color = color;
},
},
};
</script>
ColorPicker.vue
<template>
<div class="color__picker">
<h2 class="color__picker--title">Card background colour:</h2>
<input
type="color"
:value="value"
#input="$emit('input', $event.target.value)"
/>
</div>
</template>
<script>
export default {
name: "ColorPicker",
props: ["value"]
};
</script>
<style>
</style>
ProfileSlider.vue
(I take some dummy code for this component as you didn't mention in the question but the logic would be the same.)
<template>
<div class="card">
<div class="container" :style="{ background: color }">
<h4><b>John Doe</b></h4>
<p>Architect & Engineer</p>
</div>
</div>
</template>
<script>
export default {
name: "ProfileCardSlider",
props: {
color: {
required: true,
},
},
};
</script>
<style>
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
width: 40%;
}
.container {
padding: 2px 16px;
}
</style>
You should see that when you will change the color from the color input which is inside ColorPicker.vue, the card inside ProfileSlider.vue will have an updated color.

How can I duplicate my component in vue.js?

<template>
<!--This is my main file -->
<div id="inputs">
<h1>언어 관리</h1>
<v-btn color="primary" elevation="10" large #click="duplicateEl"
>Add row</v-btn
>
<Contents />
</div>
</template>
<script>
import Contents from "./Contents.vue";
export default {
name: "LanguageMainMenu",
components: { Contents },
methods: {
duplicateEl() {
alert("You can duplicate buttons");
},
},
};
</script>
<style>
h1 {
text-align: center;
font-size: 38px;
padding-top: 20px;
margin: auto;
}
</style>
The best apprach is to use the component inside v-for.
Increment the index when the button is clicked.
Dont forget to use key inside the v-for
Working Fiddle
var example1 = new Vue({
el: '#app',
name: "LanguageMainMenu",
components: {
Contents: {
template: `<div>Contents Component</div>`,
}
},
data() {
return {
totalCount: 1,
}
},
methods: {
duplicateEl() {
this.totalCount++;
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.4/vue.js"></script>
<div id="app">
<!--This is my main file -->
<div id="inputs">
<h1>언어 관리</h1>
<button #click="duplicateEl">Add row</button>
<Contents v-for="count in totalCount" :key="`component-${count}`" />
</div>
</div>
You can add a property in data object and use v-for for render buttons.
Let method duplicateEl to change the property value.
<v-btn v-for="item in btnNumber" ....>
duplicateEl(){
this.btnNumber++
}

bootstrap-vue - show button based on boolean

When I click Launch centered modal, I expect the modal hide the buttons x,cancel and ok because showButton is false.
After I click the show button, the button of the modal should be shown because now showButton is true.
How should I do it?
App.vue
<template>
<div id="app">
<b-button v-b-modal.modal-center>Launch centered modal</b-button>
<b-modal id="modal-center" centered title="BootstrapVue">
<p class="my-4">Vertically centered modal!</p>
<button #click="setShowButton">Show Button</button>
</b-modal>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
showButton: false,
};
},
methods: {
setShowButton() {
this.showButton = true;
},
},
};
</script>
<style>
#app {
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Codesandbox
https://codesandbox.io/s/flamboyant-herschel-62wpt?file=/src/App.vue:0-587
You can use the hide-header-close and hide-footer properties. Documented here. Modal docs
<template>
<div id="app">
<b-button v-b-modal.modal-center>Launch centered modal</b-button>
<b-modal
id="modal-center"
centered
title="BootstrapVue"
:hide-header-close="!this.showButton"
:hide-footer="!this.showButton"
>
<p class="my-4">Vertically centered modal!</p>
<button #click="setShowButton">Show Button</button>
</b-modal>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
showButton: false,
};
},
methods: {
setShowButton() {
this.showButton = true;
},
},
};
</script>
<style>
#app {
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Example
https://codesandbox.io/s/hungry-architecture-zrcqj?file=/src/App.vue

How does :class="`level ${file.invalidMessage && 'has-text-danger'}`" break down?

I am following this tutorial to upload files using VueJS and Express server and I have encountered a line of code that I do not understand.
At 8:19 we are introduced to :class="`level ${file.invalidMessage && 'has-text-danger'}`". I couldn't find anything similar in the Vue documentation.
Video Example:
<div v-for="(file, index) in files" :key="index"
:class="`level ${file.invalidMessage && 'has-text-danger'}`"
>
</div>
Vue Docs:
<div v-bind:class="{ active: isActive }"></div>
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
<div v-bind:class="classObject"></div>
<div v-bind:class="[activeClass, errorClass]"></div>
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
There is nothing similar to that weird syntax in the Vue Documentation.
I understand what ${} inside back quotes marks is used for(template literals).
I understand what the code purpose is, I have tested it and works, but I do not understand how it works and what shortcuts were used.
Can somebody "translate" that line of code ?
The :class binding in Vue accepts strings:
new Vue({
el: "#app",
computed: {
classlist() {
return this.classes.join(' ')
}
},
data() {
return {
classes: [
'first-class',
'second-class'
],
domClassList: null
}
},
mounted() {
// getting and setting the classlist of the DOM element
this.domClassList = document.getElementById('classList').getAttribute('class')
}
})
.level {
background: green;
padding: 15px;
}
.first-class {
color: white;
}
.second-class {
font-weight: 700;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="classList" :class="`level ${ classlist }`">
CLASSES ADDED AS STRINGS
</div>
<div>Classes: {{ domClassList }}</div>
</div>
As you can see in the snippet above, the classes are made from one level and the join of a Vue data variable (the classes array) that is returned in a computed property (classlist).
The other part of the question is the conditional format:
new Vue({
el: "#app",
computed: {
classlist() {
return this.classes.join(' ')
}
},
data() {
return {
firstOperand: true,
classes: [
'first-class',
'second-class'
],
domClassList: null
}
},
methods: {
setDomClassList() {
// getting and setting the classlist of the DOM element
this.domClassList = document.getElementById('classList').getAttribute('class')
},
toggleOperand() {
this.firstOperand = !this.firstOperand
// this.$nextTick is required so Vue can set the data
// values before we read it from the DOM
const self = this
this.$nextTick(function() {
self.setDomClassList()
})
}
},
mounted() {
this.setDomClassList()
}
})
.level {
background: green;
padding: 15px;
}
.first-class {
color: white;
}
.second-class {
font-weight: 700;
}
/* this class is set so you can see the difference on
toggling the firstOperand variable
*/
.false {
font-style: italic
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="classList" :class="`level ${ firstOperand && classlist }`">
CLASSES ADDED AS STRINGS
</div>
<div>Classes: {{ domClassList }}</div>
<hr />
<div>
FIRSTOPERAND IS {{ firstOperand }}<br />
<button #click="toggleOperand">TOGGLE FIRSTOPERAND TO {{ !firstOperand }}</button>
</div>
</div>
The second snippet is almost the same as the first, except the && is introduced. I also created a .false CSS value so you can see the returned false.

VUE: How to use prop with CSS modules?

row.vue
<script>
export default {
name: "Row",
data() {
return {
app: [
"row",
"row-test",
"flex-center"
]
}
},
template: `
<div :class="$module[app]"><slot></slot></div>
`
}
</script>
row.scss
.row {
color: red;
}
.row-test {
color: green;
}
.flex-center {
align-items: center;
justify-content: center;
}
App.vue
<template>
<div id="app">
<row app="row-test flex-center">my row</row>
<router-view />
</div>
</template>
vue.config.js
css: {
requireModuleExtension: true,
loaderOptions: {
css: {
modules: {
localIdentName: 'hex-[HASH:HEX:3]'
}
}
},
modules: true
}
I want to declare my attributes using the app attribute, where they will be written similarly to an ordinary class. Then I want there to be an Array in row.vue, which will add only the values that I define in and convert them to CSS using CSS modules (class is randomly generated)
I'm getting this
<div app="row-test flex-center">my row</div>
Expected result
<div class="hex-g3D hex-f41">my row</div>
If you're passing app as prop to Row component, define a computed property to return the modules with the different classes:
<script>
export default {
name: "Row",
props:['app'],
computed:{
modules(){
return this.app.length? this.app.map(_class=>{
return this.$module[_class];
}):[];
}
},
template: `
<div :class="modules"><slot></slot></div>
`
}
</script>
and pass it like :
<template>
<div id="app">
<row :app="['row-test', 'flex-center']">my row</row>
<router-view />
</div>
</template>

Categories