I am migrating some jQuery code to Vue. We have a part that is like the following:
<td>
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacements">
<label for="select-clicks" class="checkbox-default" #click="setValue"></label>
</p>
</td>
ie I am using a label to adjust the UI of an input similar to this: Pure CSS Checkbox Image replacement
how would I associate my v-model with the correct input value?
<td>
<p>
<input id="select-clicks" class="select-one checkbox-default-input" type="checkbox" :checked="placement.id" #input="toggleCheckbox">
<label for="select-clicks" class="checkbox-default"></label>
</p>
</td>
I have added an id attribute to the input tag which corresponds to the label for attribute.
:checked="placement.id" - Here placement.id should correspond to a boolean value stored on your vue component's data property.
#input="toggleCheckbox" - this should be a simple method on your vue component that toggles the value. Something like this:
toggleCheckbox() {
this.placement.id = !this.placement.id;
},
You can then apply conditional classes to the element using the :class bindings. You can read more about them here: https://v2.vuejs.org/v2/guide/class-and-style.html
As #GustavMahler pointed out, Vue Guide: Form Input binding shows how to reach the goal.
#GustavMahler already provided the solution by v-bind and v-on (actually v-model is one syntax sugar which does similar things. one simple explanation at here),
Below is the steps uses v-model:
add attr=id for the checkbox, so label know which checkbox it links.
v-model to one data property which is one Array. (if default value is one Boolean, the value will be one Boolean value (true/false), it ignores the value it binds (please check the third checkbox).)
click the checkbox(label), it will toggle the values to the array.
Below is one demo (the CSS part is copied from the link in the question).
new Vue({
el: '#app',
data() {
return {
placement: {id: 'value1'},
checkedPlacements: [], // default is one Array
checkedPlacement: false // default is one Boolean
}
},
watch: {
checkedPlacements: function (newVal) {
console.log('changed', newVal)
},
checkedPlacement: function (newVal) {
console.log('changed', newVal)
}
}
})
input[type=checkbox] {
display:none;
}
input[type=checkbox] + label
{
background: #999;
height: 16px;
width: 16px;
display:inline-block;
padding: 0 0 0 0px;
}
input[type=checkbox]:checked + label
{
background: #0080FF;
height: 16px;
width: 16px;
display:inline-block;
padding: 0 0 0 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacements" id="select-clicks1">
<label for="select-clicks1" class="checkbox-default"></label>
<input class="select-one checkbox-default-input" type="checkbox" :value="'value2'" v-model="checkedPlacements" id="select-clicks2">
<label for="select-clicks2" class="checkbox-default"></label>
</p>
<p>
<input class="select-one checkbox-default-input" type="checkbox" :value="placement.id" v-model="checkedPlacement" id="select-clicks3">
<label for="select-clicks3" class="checkbox-default"></label>
</p>
</div>
Related
My form has a first name and last name that initially appear as one compact string, but split into two input fields when the user clicks on the string. I need these two input fields to disappear and revert back to the compact string format when the user clicks elsewhere on the page (anywhere other than inside those two input fields). In order to achieve this, I use a v-on directive with the blur attribute. This works well, but only if the user first clicks into one of the input fields to trigger focus. This is because blur only triggers on an input field that is already in focus. Is there either a way for me to automatically put the first input field in focus when the input element conditionally renders? Is there a better approach?
Cheers.
Here is my HTML:
<a v-if="!eitherNameSelected" #click="firstNameSelected">
{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else-if="eitherNameSelected" v-enter="focusOnFirstChild($event)">
<div class="col-md-3">
<input
#focus="firstnameselected=true"
#blur="firstnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
id="firstname"
placeholder="First Name"
v-model="person.firstname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
</div>
<div class="col-md-3">
<input
#focus="lastnameselected=true"
#blur="lastnameselected=false"
type="text"
class="form-control"
name="example-text-input-invalid is-invalid"
placeholder="Last Name"
v-model="person.lastname"
>
<div class="invalid-feedback">
Invalid feedback
</div>
And in my Javascript ...
data() {
return {
listid: 0,
listname: "",
personid: 0,
person: {},
nameselected: false,
activetab1: "main",
activetab2: "notes",
firstnameselected: false,
lastnameselected: false,
....
methods: {
firstNameSelected() {
var elem = vm.$el.getElementById('firstname');
elem.focus();
this.firstnameselected="true";
}
},
computed: {
eitherNameSelected() {
return (this.firstnameselected || this.lastnameselected);
}
},
It may not be the best solution but you could use a #click($event) in order to know where you clicked to hide or not the inputs.
This post about checking event targets can be a good start I guess.
I also coded a minimal example to help you get through your issue. I hope it will help you.
new Vue({
el: "#app",
data: {
person: {
firstname: 'PersonFirstname',
lastname: 'PersonLastname'
},
showCompactString: true
},
methods: {
onCompactStringClicked() {
this.showCompactString = false
},
// Hide the inputs if a click is triggered outside of them
onAppClicked(event) {
// Do nothing if compact string is being shown
if (this.showCompactString)
return
const fistnameInputClickedOn = event.target.matches('#firstname')
const lastnameInputClickedOn = event.target.matches('#lastname')
const anyInputClickedOn = fistnameInputClickedOn || lastnameInputClickedOn
if (!anyInputClickedOn)
this.showCompactString = true
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" #click="onAppClicked($event)">
<a v-if="showCompactString" #click.stop="onCompactStringClicked">{{person.firstname}} {{person.lastname}}</a>
<div class="row" v-else>
<div>
<input type="text" id="firstname" placeholder="First Name" v-model="person.firstname">
</div>
<div>
<input type="text" id="lastname" placeholder="Last Name" v-model="person.lastname">
</div>
</div>
</div>
Don't hesitate to leave a comment if you have trouble. I'll answer you back as fast as I can.
put an #click=someFunction (or computed) on the root element (i.e. the whole body of HTML) and then have that change the text.
I am trying to change the color of text depending on what option is chosen from a drop-down menu. This is for a TODO List project that I am working on. The drop-down menu has three options: High urgency (change text to red), Medium Urgency (change text to yellow), and Low Urgency (change text to green).
<template>
<div class="TodoList">
<input type="text" class="todo-input" v-model="newTodo" #keyup.enter="addTodo">
<div v-for="(todo, index) in todos" :key="todo.id" class="todo-item"
</div>
<ul class="urgency-column">
<li>
<label class="todo-label" >Select Urgency level:</label>
</li>
<li>
<select class="todo-drop" id="" onchange="setUrgency()">
<option value="high">High Urgency</option>
<option value="medium">Medium Urgency</option>
<option value="low">Low Urgency</option>
</select>
</li>
</ul>
</div>
</div>
</template>
<scripts>
export default {
methods: {
setUrgency(todo) {}
}
}
</script>
First, use v-model to capture the selected urgency. Make sure the model includes an urgency property when adding a new TODO item:
methods: {
addItem() {
this.todos.push({
urgency: '',
//...
})
}
}
<select class="todo-drop" v-model="todo.urgency">...</select>
Class binding
You could use a class binding to set a specific class based on the value of the item's urgency value:
<div class="item-text"
:class="{ high: todo.urgency === 'high', medium: todo.urgency === 'medium', low: todo.urgency === 'low' }">
{{todo.text}}
</div>
Then in your <style> block, style the item's text according to the corresponding urgency class:
.item-text.high {
color: red;
}
.item-text.medium {
color: yellow;
}
.item-text.low {
color: green;
}
Attribute binding
Or you could apply an attribute that could be selected in CSS. For example, this adds to the TODO item's text container an urgency attribute with a value equal to the selected urgency:
<div class="item-text" :urgency="todo.urgency">{{todo.text}}</div>
Then in your <style> block, use an attribute selector to style the item text by urgency:
.item-text[urgency="high"] {
color: red;
}
.item-text[urgency="medium"] {
color: yellow;
}
.item-text[urgency="low"] {
color: green;
}
I am using vueJs and have a radio button group. When the radio is checked how can I bind a css border attribute to a class :class="selected"?
The :checked attribute does not work as I have bound it to the v-model.
So if the radio is checked bind a class (selected) to the div.
<div class="sau-select lg center" :class="selected">
<label for="windows">
<input type="radio" id="windows" value="windows" v-model="selectedOs" :checked="checked">
<span></span>
<img src="/img/windows.gif" alt="Windows">Windows
</label>
</div>
The :class="selected" you use hasn't much effect.
To conditionally apply a class to the div, you will have to use as :class condition if the selectedOs equals the value attribute of each <input>, such as :class="{redborder: selectedOs === 'windows'}". More details in the demo below:
new Vue({
el: '#app',
data: {
selectedOs: 'windows'
}
})
.redborder { border: 1px solid red; }
<script src="https://unpkg.com/vue"></script>
<div id="app">
<label :class="{redborder: selectedOs === 'windows'}">
<input type="radio" value="windows" v-model="selectedOs"> Windows
</label>
<label :class="{redborder: selectedOs === 'linux'}">
<input type="radio" value="linux" v-model="selectedOs"> Linux
</label>
</div>
Notice that value="linux" means that that radio box will assign the string "linux" to the v-model variable (in this case selectedOs). value="linux" is equivalent to :value="'linux'", for instance.
I want to show red on r.data2 by vue.js, but it can not work proper, any hint to debug it?
The data 'rows' is updated by a timer per second. The 'Status', 'data1', 'data2' are generated on server side.
vm = new Vue({
el: '#app',
data: {
rows: [{Status:1, data1:"d1", data2:"d2"}]
}
})
...
<tr v-for="r in rows">
<td><button class="st-{{r.Status}}">r.data1</button></td>
<td valign="top">
<div v-if="r.Status == 4">
<font color="red">{{r.data2}}</font>
</div>
<div v-else>
{{r.data2}}
</div>
</td>
</tr>
Did you try class and style bindings as given in Vue docs?
Ref: https://vuejs.org/guide/class-and-style.html#Object-Syntax-1
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
and your data:
data: {
activeColor: 'red',
fontSize: 30
}
That example is a copy-paste from the docs link above. Check it out!
Edited answer after comment #1:
The best way is to use array syntax for class bindings, as follows:
<div v-bind:class="['some-static-class-for-your-data', { 'danger-class' : data2 == 4 }]">
my data2 value = {{data2}}
</div>
And set a CSS like:
.danger-class {
color: red;
}
Now whenever your data2 has the value of 4, the 'danger-class' will be set on your div. Your CSS ensures that it is displayed in red, or whatever color you choose.
If you have other classes for your data, you can put it as shown in the above example with some-static-class
It is a good practice to set meaningful class names instead of hard-coding styles. So I would not recommend you to go for style bindings, though you can do it if you want. Check out the docs link for class and style bindings.
On input focus I want to change the color of the label element. How can I achieve this in less?
.control-label{
color: #gray-light;
}
.controls{
input,
textarea{
background-color:red;
&:focus{
.control-label &{
color: red; //HERE
}
}
}
HTML:
<div class="control-group">
<label class="control-label" for="inputEmail">Firstname</label>
<div class="controls">
<input type="text" id="inputEmail" placeholder="Firstname">
</div>
</div>
One solution would be to use the :focus-within selector.
So, you'd do something like the below. Assuming that you always have an input of some description inside of a control-group, it will style the label whenever the input is focused on.
control-group {
&:focus-within {
control-label {
color: red;
}
}
}
More information can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within
I don't think you can without changing your HTML, see also: Is there any way to hover over one element and affect a different element?, your elements should be direct siblings. (LESS don't help to solve your problem here, LESS generate CSS and it seems impossible to do in CSS)
Possible suggestion:
input:focus + .control-label
{
background-color:purple;
color: red;
}
.controls > input
{
float:right;
}
<div class="controls">
<input type="text" id="inputEmail" placeholder="Firstname">
<label class="control-label" for="inputEmail">Firstname</label>
</div>
Or solve your problem with javascript: https://stackoverflow.com/a/20226218/1596547
Use Flexbox
CSS is cascading, i.e. affected by the order that elements appear in the DOM. To be able to select the label only when the input is focused (input:focus + label), the label needs to come after the input, so;
Put the input before the label in the DOM and then use flexbox to reverse the order that they appear on the page.
.input-group {
display: flex;
flex-direction: column-reverse;
}
input:focus + label {
color: green;
}
<div class="input-group">
<input value="Input" />
<label>Label</label>
</div>
One solution would be to move the label below the input in the DOM but position them absolutely (to the parent) so the label looks to be above the input field:
<div>
<input type="text"/>
<label>Text</label>
</div>
In CSS move the label to the top, the input to the bottom:
label {
position: absolute
top: 0
}
input {
position: absolute
bottom: 0
}
And use the :focus state to change the style of the label:
input:focus + label {
color: red
}
See example:
http://codepen.io/robcampo/pen/zGKLgg
On focus, the label turns red. No JS required.
control-group {
&:focus-within {
control-label {
color: red;
}
}
}
loads of hearts to this person. Nothing was working and this worked in ion-label too . Accepted answer. Stack overflow does not allow to comment (below 50 reputation). So writing seperately <3
I hope I am not too late to answer this. With the new :has psudo-class we can achieve it without changing the HTML,
HTML:
<div class="control-group">
<label class="control-label" for="inputEmail">Firstname</label>
<div class="controls">
<input type="text" id="inputEmail" placeholder="Firstname">
</div>
</div>
CSS:
.control-group:has(input:focus) label{
color: red;
}
This approach is simple and cleaner.
Note: it is not supported by firefox yet.
The easy way is to use :focus-within check developer mozilla
.control-group:focus-within .control-label {
color: red;
}
Or
.control-group:focus-within label {
color: red;
}