See https://jsfiddle.net/d017am59/1/
I have a checkbox controling whether box elements should show text in black.
<div id="app">
<label><input type='checkbox' v-model='showBlack' />Show black</label>
<box>Hello</box>
<box>World</box>
<box>Hi</box>
<box>Bye</box>
</div>
Vue.component('box', {
template: `
<div v-bind:style='styleObject'>
{{text}}
<slot></slot>
</div>`,
data: function() {
return {
text: vueApp.showBlack?'black text: ':'white text: ',
styleObject:{
color: vueApp.showBlack?'black':'white',
'background-color':vueApp.showBlack?'white':'black',
}
}
}
});
const vueApp= new Vue({
el: '#app',
data: {
showBlack: true,
},
});
box is a complicated component, but I only present the relavent props here. For illustration, if the checkbox is checked, the color is black, the background is white; if the checkbox is unchecked, the color and the background color is reversed.
My code doesn't work because Vue throws an error "Cannot access 'vueApp' before initialization."
How do my box elements listen to the checkbox?
I don't like using a global mixin so much because a global mixin is going to inject to all components, where I only want to inject to box.
I don't like adding a prop to box, and pass showBlack to the prop of all box instances. My business requirement is ALL boxes must obey the checkbox, and it is cumbersome and error-prone if I have to write:
<box v-bind:showBlack="showBlack">Hello</box>
<box v-bind:showBlack="showBlack">Wolrd</box>
<box v-bind:showBlack="showBlack">Hi</box>
<box v-bind:showBlack="showBlack">Bye</box>
I'm fine to use Vue 2 or 3.
A quick solution is to use this.$root to access the data of the root instance (the one that instantiates Vue). Also, note the bindings in <box> should be to computed props so that they're reactive to changes in the root's showBlack:
Vue.component('box', {
template: `
<div v-bind:style='styleObject'>
{{ text }}
<slot></slot>
</div>`,
computed: {
text() {
return this.$root.showBlack ? 'black text: ' : 'white text: '
},
styleObject() {
return {
color: this.$root.showBlack ? 'black' : 'white',
'background-color': this.$root.showBlack ? 'white' : 'black',
}
}
}
})
demo
HTML:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div><label><input type='checkbox' v-model='showBlack' />Show black</label></div>
<box :black="showBlack">Hello</box>
</div>
JS:
Vue.component('box', {
template: `
<div v-bind:style='styleObject'>
<slot></slot>
</div>
`,
props: ['black'],
data: function() {
return {}
},
computed: {
styleObject() {
return {
color: this.black ? 'black' : 'white',
'background-color': this.black ? 'white' : 'black',
}
}
}
});
const vueApp= new Vue({
el: '#app',
data: {
showBlack: true,
},
});
Related
How can I add the following styles as props for the component?
<template>
<div>
<transition-group name='fade' tag='div'>
...
</div>
</template>
<script>
export default {
name: 'Slider',
data() {
return {
}
props: { intervalVal: Number,
width...
},
},
</script>
styles>
div {
width: 100%
height: 600px
}
.prev,
.next {
color #fffff
background-color:#22222
How can I put that divs with and height as props as well as prev. .next class color and background-colors?
when you only want to style your tag with the prop i would do it with
<div :style="`width: ${this.width}`">
If you want to add css classes dynamically you can use objects for it like described here
in your template
<div v-bind:class="{ active: isActive }"></div>
and in your component
data: {
isActive: true,
hasError: false
}
you can also use computed propteries to do it dynamically
In vue.js how can I filter products by size, color, category & by selecting each of these categories in step-wise slider where I have to select each filter one by one step-wise & at last I need to display the products which pass through the filter.
Please share code examples and ask specific questions about where you are stuck. This question is very broad and is difficult to answer.
I'll try to provide an answer as best as I can.
You might have a ProductList component that may look something like this:
<template>
<!-- your template -->
</template>
<script>
import RangeSlider from "./RangeSlider";
export default {
name: "ProductList",
components: {
RangeSlider
},
data() {
return {
products: [],
filters: {
color: null,
size: null,
category: null
},
options: {
color: ["Red", "Yellow", "Green"],
size: ["Small", "Medium", "Large"],
category: ["Men", "Women", "Boy", "Girl"]
}
};
}
};
</script>
The RangeSlider can use the native <input type="range" /> control to build the functionality you want.
In this example, it takes a label and an array of options to build the control, and will emit a change event when the user changes the value of the control by sliding, which is captured in ProductList to set the filters. You can then use the filters object and make an API call to fetch your products.
<template>
<div class="range-slider">
<label>
<strong>{{ label }}</strong>
</label>
<div>
<input type="range" min="0" :max="max" step="1" #change="onInputChange" :value="rangeValue">
</div>
<div>{{ options[rangeValue] }}</div>
</div>
</template>
<script>
export default {
name: "RangeSlider",
props: ["label", "options"],
data() {
return {
rangeValue: 0
};
},
mounted() {
this.onChange(0);
},
methods: {
onInputChange(e) {
this.onChange(e.target.value);
},
onChange(rangeValue) {
this.rangeValue = rangeValue;
let selectedOption = this.options[this.rangeValue];
this.$emit("change", selectedOption);
}
},
computed: {
max() {
return this.options.length - 1;
}
}
};
</script>
<style>
.range-slider {
padding: 1rem;
border-bottom: 1px solid;
}
</style>
Here is the working example on CodeSandbox: https://codesandbox.io/s/rangesliderwithoptionsexample-6pe10
I am generating small list of items. After click on every single item it should change style. Only one item can be selected. If you click on another item first item reverse to default value.
I have follow code:
<div class="LngList">
<div class="lng" v-for="item in languages">
<button :class="[ isLangSelected ? 'ui inverted basic button' : 'ui inverted red basic button' ]" #click=LangSelect(item.lang)>{{item.lang}}</button>
</div>
</div>
My method:
data: function (){
return {
isLangSelected: false,
mycode: "",
languages: [
{lang:'D'},
{lang:'C#'},
{lang:'Python'}
],
selectedLanguage: ""
}
},
methods: {
LangSelect(lang)
{
this.selectedLanguage = lang;
if(this.selectedLanguage.length != "")
{
this.isLangSelected = !this.isLangSelected;
}
}
}
But when I am clicking outside the button I am losing selected style.
I did small gif to show the problem:
This is possible of course with buttons, but why don't you use a radio input instead? Having only one item selected, that's what they are done for.
new Vue({
el: '#app',
data() {
return {
languages: [{
lang: 'D'
},
{
lang: 'C#'
},
{
lang: 'Python'
}
],
selectedLanguage: ''
};
},
computed: {
isLangSelected() {
return this.selectedLanguage !== '';
}
}
});
input[type=radio] {
visibility: hidden;
width: 0;
}
input[type=radio]:checked + span {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<label v-for="lang in languages">
<input type="radio" name="languages"
v-model="selectedLanguage" :value="lang.lang">
<span>{{lang.lang}}</span>
</label>
<div v-if="isLangSelected">
Selected language is: {{ selectedLanguage }}
</div>
</div>
I have read about it:
vuejs update parent data from child component
https://forum.vuejs.org/t/passing-data-back-to-parent/1201/2
The concept is the same, I need to pass a data object from child to parent. I have used $emit to pass data to parent component but it doesn't works. Do you know what is wrong? You can check my code here:
Vue.component('list-products', {
delimiters: ['[[', ']]'],
template: '#list-products-template',
props: ['products'],
data: function () {
return {
productSelected: {}
}
},
methods: {
showDetailModal: function (product) {
console.log('click product in child, how can i pass this product to productSelected data in parent?');
console.log(product);
this.productSelected = product;
this.$emit('clickedShowDetailModal', product);
}
}
});
var app = new Vue({
delimiters: ['[[', ']]'],
el: '#resultComponent',
data: {
listProducts: [
{'name':'test1',id:1},
{'name':'test2',id:2},
{'name':'test3',id:3}
],
productSelected: {}
},
methods: {
clickedShowDetailModal: function (value) {
console.log('value');
console.log(value);
this.productSelected = value;
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="resultComponent" data-toggler=".small-up-2" class="row small-up-1">
<list-products :products="listProducts"></list-products>
</div>
<script type="text/x-template" id="list-products-template">
<div>
<div class="column column-block" v-for="(product, index) in products" :product="product" :index="index" :key="product.id">
<li class="more-benefits">
<a #click="showDetailModal(product)">Click me [[ product.name ]] and check console.log ยป</a>
</li>
</div>
</div>
</script>
Props are for parent -> child
You can use $emit for child -> parent
v-on directive captures the child components events that is emitted by $emit
Child component triggers clicked event :
export default {
methods: {
onClickButton (event) {
this.$emit('clicked', 'someValue')
}
}
}
Parent component receive clicked event:
<div>
<child #clicked="onClickChild"></child>
</div>
...
export default {
methods: {
onClickChild (value) {
console.log(value) // someValue
}
}
}
You aren't listening to the event. I changed the event name to clicked-show-detail. Try this.
In the showDetailModal method of your component.
this.$emit('clicked-show-detail', product);
In your Vue.
<list-products :products="listProducts" #clicked-show-detail="clickedShowDetailModal"></list-products>
Example.
Nightmare to find "hello world" example out there for $emit so I added the example below (Minimal lines of code + semantic names of functions).
"Hello world" On click change parent data
Vue.component('child', {
template: `
<div class="child">
<button v-on:click="childMethod">CLICK - child Method pass data from product component</button>
</div>
`,
data: function () {
return {
child_msg: "message from child"
}
},
methods: {
childMethod: function() {
this.$emit('child-method', this.child_msg)
}
}
})
var app = new Vue({
el: '#app',
data: {
msg: "I am the blue parent!!!!!!!!!!!!!!!!!!",
},
methods: {
updateParent(value_from_child) {
this.msg = value_from_child;
alert("hello child" + value_from_child)
}
}
})
.child{ background: gray; padding: 15px; }
button{ cursor: pointer; }
#app{ border: 1px red dashed; padding: 15px; background: lightblue; color: blue;
}
<div id="app">
<p>{{msg}}</p>
<!-- ###### The trick happens her ###### -->
<child class="child" v-on:child-method="updateParent"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
codepen: https://codepen.io/ezra_siton/pen/YzyXNox?editors=1010
I don't have much knowledge about child and parent component and i am just simply trying to change value in child then emit then value to parent so i can show somewhere, But it looks like not working, Not emiting the value.
This should change to : Hello from child
{{ message }} From Parent
Can anyone look this code and tell me what is mistake ?
Vue.component('child1', {
template: '<p #click="runMe">{{ display }}</p>',
props: ['display'],
data: {
display: ''
},
methods: {
runMe() {
this.display = "Hello from child"
this.$emit("changeMessage", this.display)
}
}
})
new Vue({
el: "#app",
data: {
message: "Hello 2"
},
methods: {
messageRun() {
this.message = "Change By"
}
}
})
.btnMain {
display: block;
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.2/vue.min.js"></script>
<div id="app">
<button #click="messageRun" class="btnMain">Click Here</button>
<child1 :display="message" #changeMessage="message = $event"></child1>
<hr>
{{ message }} From Parent
</div>
In your code you have this where you emit:
this.$emit("changeMessage", this.display)
Change to :
this.$emit("newmessage", this.display)
I mean use lower case single word, if you use camelCase vue convert it to change-message but this is not acceptable by attribute, I tried this on your code.
After change this do this :
<child1 :display="message" #newmessage="message = $event"></child1>