How to reactively set a <html> class - vue.js

I have 2 themes: 'light-theme', 'dark-theme'
I would like to set the <html class=""> according to a value i have in my vuex store. Like darkMode: true
How can i set the <html> class reactively like that?
(using vue3)

You can use simple javascript for it.
Just add a button or toggle for theme change like:
<button #click="changeTheme">Change Theme</button>
and inside this component add changeTheme method inside methods:
document.getElementsByTagName("html")[0].setAttribute( 'class', 'newThemeClass' );
Or just add watcher to your store value and change according to it:
computed:{
...mapGetters(["yourGetterName"])
},
watch: {
darkMode(value) {
document.getElementsByTagName("html")[0].setAttribute( 'class', value );
},
}

According to Vue docs, there are following ways to do this.
1.Binding to Objects
<div :class="{ 'active-class': isActive }"></div>
The above syntax means the presence of the 'active-class' class will be determined by the truthiness of the data property isActive.
2.Binding to Arrays
<div :class="[isActive ? activeClass : '', errorClass]"></div>
This will always apply errorClass, but activeClass will only be applied when isActive is truthy.

Related

adding a custom class to the div element

i have a custom component where i am passing some data to render some css styles
like
<title :profile="true" :class="true"/>
in my component
i have a div as:
<div class="tabletitle">
i want if my custom class :class is true, i should add a new class called as flexdisplay to it like
<div class="tabletitle flexdisplay">
what i am missing here, i tried passing the value to data as false but it just throwing random errors
Lets assume your parent component is like
<title :profile="true" :showClass="true"/> <!-- modified props name from class to showClass
and in your child component, as you said you have a div like below
<div class="tabletitle">
what you can do is change the above div to the below format
<div :class="['tabletitle', {'flexdisplay': enableClass}]"> <!-- where enableClass will be a computed property
Inside the script tag of your child component, define props and computed properties like below
<script>
export default {
props: {
showClass: {
type: Boolean,
default: false
},
}
computed: { // The reason I am doing with computed is for better reactivity
enableClass() {
return this.showClass;
}
}
}
</script>
You can use class condition syntax
<div :class="{ "active": isActive }"></div>
This will put the active class if the isActive condition is true
Note that you can combine the class and :class attributes to put either class conditionnaly and classic class
<div class="myClass" :class="{ "active": isActive }"></div>
For example in your case if you have the boolean class props your component will look like this :
<template>
<div class="tabletitle" :class={"flexdisplay": isClass}>
...
</div>
</template>
<script>
export default {
props: {
isClass: {
type: Boolean,
default: true
}
}
}
</script>
**Note : ** i've changed the class props by isClass to better understand and do not confuse with the class keywork

how to be not hardcoded for Buefy type in vue.js

<b-checkbox v-model="selectedTiers" :native-value="i" type="checkType(i)" #input="update">
{{ $t('general.specificTier', { tier: i }) }}
</b-checkbox>
Hi everyone, I am using Buefy and Vue.js. I want the type to be flexible. That is why I am using the method here. according to different I, it outputs a different string. But type here doesn't recognize the method here. I also can't use string + string here.
Here is information about checkbox of buefy.
You can use v-bind directive to dynamically alter the attributes.
Here is an example to get your started:
<template>
<div id="app">
<!-- Example with string manipulation -->
<b-checkbox :type="`is-${type}`">TEST 1</b-checkbox>
<!-- Example by reading off compenent-data -->
<b-checkbox :type="checkboxType">TEST 2</b-checkbox>
</div>
</template>
<script>
export default {
name: "App",
components: {},
data() {
return {
type: 'success',
checkboxType: "is-success"
};
}
};
</script>
One last thing, you can still use a method there (just add : before the attribute name - like :type="checkType(i)"), but the function would only be evaluated once and any further data changes won't be reflected on the type attribute (won't update on data change)

Vue - adding prop as a Bem modifier

I am passing in a 'type' prop to my component to use as a BEM modifier.
<div class="badge badge--{{this.$props.type}}">
However when I try and concatenate the class I get an error. How do I achieve this?
A cleaner way to do it is to create a computed property :
computed: {
badgeClasses() {
return `badge badge--${this.type}`;
},
},
And then bind it in your HTML :
<div :class="badgeClasses">
But you can also just bind the property, as you tried, but with a backquote instead of a simple quote :
<div class="`badge badge--${type}`">

Vue.js Input value gets deleted on first char only

Based on the official example Custom Input Component example:
HTML:
<div id="app" >
<div :class="{'has-value' : hasValue}">
<input type="text"
ref="input"
v-bind:value="value"
#input="onInput" >
</div>
</div>
JS:
new Vue({
el: '#app',
data: function() {
return {
hasValue: false
}
},
props: ['value'],
methods:{
onInput: function(){
val= this.$refs.input.value
if(val && val.length > 0){
this.hasValue = true
} else {
this.hasValue = false
}
}
}
})
JS Fiddle to play
Expected:
Dynamically set hasValue data for input and bind a Css Class to this data
Unexpected:
Only after initialization and typing the first Character, the typed character gets deleted from the input. Press JS-Fiddle->Run to see it again.
After first character everything works as expected.
If I remove v-bind:value="value" or comment \\this.hasValue = true it works as expected. Binding value prop is recommended for custom Input component.
Is this intended and why? Or is this a bug I should report?
This is expected behaviour:
When the first character is inserted, hasValue is changed from false to true, causing the component to re-render
During re-render, Vue sees that the input has a value (the character you just typed in), but the property you have bound to it (the value prop) is empty.
Therefore, Vue updates the input to match the bound prop - and thus, it empties the input.
After that, hasValue doesn't change anymore, so there's no re-rendering happening, and thus, Vue doesn't reset the input field's value anymore.
So how to fix this?
First you have to understand that Vue is data-driven - the data determines the HTML, not the other way around. So if you want your input to have a value, you have to reflect that value in the property bound to it.
This would be trivial if you worked with a local data property:
https://jsfiddle.net/Linusborg/jwok2jsx/2/
But since you used a prop, and I assume you want to continue using it, the sittuation is a bit different - Vue follows a pattern of "data down - events up". You can't change props from within a child, you have to tell the parent from which you got it that you want to change it with an event. The responsibility to change it would be the parent's. Here's your example with a proper parent-child relationship:
https://jsfiddle.net/Linusborg/jwok2jsx/4/
I'm guessing that when you modified hasValue, Vue re-renders the component (to apply the has-value class to the div) which causes the v-bind:value to be re-evaluated. value is a prop which is unassigned and never changes, so the value of the input element gets cleared.
You really should use v-model on the input element and control has-value based on the value in the model, rather than interacting directly with the DOM. (You won't be able to use v-model with value though since value is a prop and cannot be assigned from within the component.)
I'm also guessing you want to make a custom input component which applies a certain style when it has a value. Try this:
Vue.component('my-input', {
props: ['value'],
template: `<input class="my-input" :class="{ 'has-value': value }" type="text" :value="value" #input="$emit('input', $event.target.value)">`,
});
new Vue({
el: '#app',
data: {
value: '',
},
});
.my-input.has-value {
background-color: yellow;
}
<script src="https://unpkg.com/vue#2.5.6/dist/vue.js"></script>
<div id="app">
<my-input v-model="value"></my-input>
</div>
Are you trying to achieve something like this?
new Vue({
el: '#app',
data: {
input: ''
},
computed: {
hasValue () {
return this.input.length ? true : false
}
}
})
.has-value {
background-color: red;
}
<div id="app">
<div :class="{'has-value' : hasValue}">
<input type="text" v-model="input">
</div>
</div>
<script src="https://unpkg.com/vue"></script>

Trying to create generic input component

I have a lot of forms to create in a web app I'm working on, for which I'm using Vue, so I've been trying to create a generic input component I can use throughout. I'm using Bootstrap grids, so the idea is that I should be able to pass the component a number of columns to take up, a label, a name and a property to use as the v-model. I'm kind of getting there, I think, but I'm running into a problem with mutating props - [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "model"
(found in component ).
Here's the template (in simplified form):
<template id="field">
<div v-bind:class="colsClass">
<div class='form-group form-group-sm'>
<label v-bind:for="name">{{labelText}}</label>
<input v-bind:id='name' ref="input" class='form-control' v-bind:name='name' v-model='model'/>
</div>
</div>
And here's the (again simplified) JS:
Vue.component('field', {
template: '#field',
props: ['cols','label','group','name','model'],
computed:{
colsClass:function(){
return "col-xs-"+this.cols
}
,
labelText:function(){
if(this.label) {
return this.label
} else {
return _.startCase(this.name);
}
}
}
});
This is used from within another 'edit-product' component, like this:
<field :cols="8" name="name" :model="product.name"></field>
This displays OK, but throws the error (or more accurately, warning), when I edit the value of the field. So what am I doing wrong?
Actually, the solution I've gone for is rather simpler than the one suggested above, very simple in fact, taken from https://forum-archive.vuejs.org/topic/4468/trying-to-understand-v-model-on-custom-components/9.
I don't want the 'model' prop, I have a 'value' one instead, so the JS is changed to this:
Vue.component('field', {
template: '#field',
props: ['cols','label','group','name','value'],
computed:{
colsClass:function(){
return "col-xs-"+this.cols
}
,
labelText:function(){
if(this.label) {
return this.label
} else {
return _.startCase(this.name);
}
}
}
});
The template becomes this:
<div class='form-group form-group-sm'>
<label :for="name">{{labelText}}</label>
<input :id='name' :name='name' class='form-control' :value="value" #input="$emit('input', $event.target.value)"/>
</div>
</div>
And I use it like this:
<field :cols="8" name="name" v-model="product.name"></field>
The difference is that I'm not actually trying to pass a model prop down, I'm just passing a value, and listening for changes to that value. It seems to work pretty well and is clean and simple enough. My next challenge is passing an arbitary set of attributes to the input, but that's the subject of another question.
As the warning suggests you should not directly edit the prop you are passing for the value.
Instead use this as the original value and set a seperate value on the input from it - which you can pass to the v-model. If you need the parent to have the current value then also pass a prop that will allow you to update the param on the parent, i.e.
input component
# script
props: [
'origValue',
'valueChange',
],
data: {
inputValue: '',
...
},
mounted () {
this.inputValue = this.origValue
},
watch: {
inputValue () {
this.valueChange(this.inputValue)
},
...
},
...
# template
<input type="text" v-model="inputValue">
parent
# script
data () {
return {
fieldValue: 'foo',
...
},
},
methods: {
updateField (value) {
this.fieldValue = value
},
...
},
...
# template
<field :value-change="updateField" :orig-value="fieldValue"></field>