Getting textbox to turn red on change - vue.js

I'm trying to make my textbox red when someone starts adding text to it. The problem I'm having is it takes long
to hit my onChange method and change the text color red.
Here is my code
<template>
<div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Name</label>
<input
type="text"
class="form-control"
v-model="product.name"
#change="onChange"
:style="{ color: conditionalColor}"
>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['product'],
computed: {
conditionalColor(){
return this.dirty ? 'red' : ''
}
},
data() {
return {
dirty: false
}
},
methods: {
onChange(){
console.log('changing');
return this.dirty = true;
}
},
mounted() {
}
}
</script>

Try watching product.name and setting this.dirty = true; there. The first time you edit the input after mounting it will fire and accomplish the same as your onChange method. Additionally, you can save some steps by conditionally applying a CSS class instead of computing it.
<template>
<input type="text" class="form-control" :class="{ 'dirty': dirty }" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>
<script>
export default {
props: ['product'],
data() {
return {
dirty: false
}
},
watch: {
'product.name': function() {
this.dirty = true;
}
},
}
</script>

This is also alternative way to style your input.
<template>
<input type="text" class="form-control" :class="{ 'dirty': product.name.length > 0}" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>

Related

I am converting my code snippet from pure html to Vue2 but facing some difficulty

This is pure html code with javascript.
filter.js
var group_filter=document.getElementById("group-filter");
var btn_show_filter=document.getElementById("icon-filter");
var btn_close_filter=document.getElementById("icon-close-filter");
function showfilter(){
group_filter.style.display='block';
btn_show_filter.style.display='none';
btn_close_filter.style.display='inline';
}
function closefilter(){
group_filter.style.display='none';
btn_show_filter.style.display='inline';
btn_close_filter.style.display='none';
}
And here is the code that I'm trying to process but it doesn't look right.
<div class="job-filter">
<h3>Filter <img id="icon-filter" #click="showfilter" :class="{ 'display-none': this.display }" src="../assets/recruit/angle-down-svgrepo-com.svg" alt=""> <img id="icon-close-filter" :class="{ 'display-inline': this.display }" #click="showfilter" src="../assets/recruit/close-svgrepo-com.svg" alt=""></h3>
<div class="radio-group" id="group-filter" :class="{ 'display-block': this.display}" >
</div>
</div>
Thank you everyone, and I look forward to your help.
You can simply achieve this by creating an object in the data method like this :
data() {
return {
display: {
group_filter: '',
btn_show_filter: '',
btn_close_filter: ''
}
}
}
And in methods object, you can assign the values :
methods: {
showfilter() {
this.display.group_filter = 'block';
this.display.btn_show_filter = 'none';
this.display.btn_close_filter = 'inline';
},
closefilter() {
this.display.group_filter = 'none';
this.display.btn_show_filter = 'inline';
this.display.btn_close_filter = 'none';
}
}
And then in the template, you can assign like this :
<div class="job-filter">
<h3>Filter <img id="icon-filter" #click="showfilter" :style="{ 'display': display.btn_show_filter }" src="../assets/recruit/angle-down-svgrepo-com.svg" alt=""> <img id="icon-close-filter" :style="{ 'display': display.btn_close_filter }" #click="closefilter" src="../assets/recruit/close-svgrepo-com.svg" alt=""></h3>
<div class="radio-group" id="group-filter" :style="{ 'display': display.group_filter }"></div>
</div>
I hope this is what you want to achieve.
I have create one demo on stackblitz.
link - https://vue-etrbkr.stackblitz.io
I have attached source code here.
<template>
<div id="app">
<div class="job-filter">
<h3>
Filter
<img
#click="toggleFilter"
class="filterIcon"
:src="
isFilterOpen
? 'https://cdn-icons-png.flaticon.com/512/61/61155.png'
: 'https://iaeste.org/assets/icons/arrow_dropdown-
8e096a444299e295fa03bc919f3cea625987792e13aaf64ec6b0be881a8f7c0a.svg'
"
width="20"
height="20"
/>
</h3>
<div class="radio-group" id="group-filter" v-if="isFilterOpen">
Filter Content
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
isFilterOpen: false,
};
},
methods: {
toggleFilter() {
this.isFilterOpen = !this.isFilterOpen;
},
},
};
</script>
<style>
.filterIcon {
vertical-align: bottom;
cursor: pointer;
}
</style>

How to restrict user to enter only 30 characters in Vuejs?

<script>
export default {
name: "Register",
props: {
msg: String,
},
};
</script>
-------------main.js---------------
new Vue({
data:{
max:30,
text:''
},
render:h => h(App),
}).$mount('#app'
<template>
<div class="pop-up-mask">
{{ msg }}
<div class="pop-up">
<input type="text" class="input-section"
placeholder="Enter your Name" :maxlength="max" v-model="text" />
</div>
</template>
If the user tries to enter more than 30 characters, user should get an error message: you can only enter 30 characters. Try with above logic like maxlength="max" v-model="text"
I had done something similar in the past, so I built on that component (plus some research) to build this component that solves the problem.
<template>
<div class="input-max">
<div class="form-row">
<div class="col-md-8">
<input class="form-control" type="text" placeholder="Address"
v-model="address" #keyup="updateAddress">
</div>
<div class="col-md-4">
<span v-if="displayWarning" class="error-msg">* You can only enter {{ maxLength }} characters</span>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
address: '',
previousAddress: '',
maxLength: 30,
displayWarning: false
}
},
methods: {
updateAddress(event) {
let newValue = event.target.value;
if (newValue.length > this.maxLength) {
event.preventDefault()
this.address = this.previousAddress;
this.displayWarning = true;
}
else {
this.address = newValue;
this.previousAddress = newValue;
this.displayWarning = false;
}
}
}
}
</script>
<style scoped>
.error-msg {
color: red;
}
</style>

Component with optional value prop

I'm writing a re-usable component. It's basically a section with a header and body, where if you click the header, the body will expand/collapse.
I want to allow the consumer of the component to use v-model to bind a boolean to it so that it can expand/collapse under any condition it wants, but within my component, the user can click to expand/collapse.
I've got it working, but it requires the user of the component to use v-model, if they don't, then the component doesn't work.
I essentially want the consumer to decide if they care about being able to see/change the state of the component or not. If they don't, they shouldn't have to supply a v-model attribute to the component.
Here's a simplified version of my component:
<template>
<div>
<div #click="$emit('input', !value)">
<div>
<slot name="header">Header</slot>
</div>
</div>
<div :class="{ collapse: !value }">
<div class="row">
<div class="col-xs-12">
<div>
<slot></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
#Component
export default class CollapsibleSection extends Vue {
#Prop({ default: true }) public value: boolean;
}
</script>
Update:
I've come up with a solution that meets my requirements functionally. It's a little more verbose than I would like, so if anyone has a more terse solution, I would love to read about it, and I will gladly mark it as the accepted answer if it meets my requirements with less code/markup.
<template>
<div>
<div #click="toggle">
<div>
<slot name="header">Header</slot>
</div>
</div>
<div :class="{ collapse: !currentValue }">
<div class="row">
<div class="col-xs-12">
<div>
<slot></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
#Component
export default class CollapsibleSection extends Vue {
#Prop({ default: true }) public value: boolean;
public currentValue = true;
public toggle() {
this.currentValue = !this.currentValue;
this.$emit('input', this.currentValue);
}
public mounted() {
this.currentValue = this.value;
}
#Watch('value')
public valueChanged() {
this.currentValue = this.value;
}
}
</script>
Your update works and has the right gist in general, but instead of a watcher it would be better to use a computed property. See the docs for computed properties and watchers for more info.
I've excluded the class notation in the below snippet to have it runnable on-site.
Vue.component('expandable', {
props: {
value: {
// Just to be explicit, not required
default: undefined,
validator(value) {
return typeof value === 'boolean' || typeof value === 'undefined';
},
},
},
template: `
<div class="expandable">
<p #click="toggle()">toggle</p>
<slot v-if="isOpen" />
</div>
`,
data() {
return {
internalValue: true,
};
},
computed: {
isOpen() {
return (typeof this.value !== 'undefined') ? this.value : this.internalValue;
},
},
methods: {
toggle() {
this.internalValue = !this.internalValue;
this.$emit('input', !this.isOpen);
}
}
});
new Vue({
el: '#app',
data() {
return {
isOpen: false,
}
}
})
.expandable {
border: 2px solid blue;
margin-bottom: 1rem;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<expandable>
<p>no model</p>
</expandable>
<expandable v-model="isOpen">
<p>has model</p>
</expandable>
</div>

Editing a form with save and cancel options

I'm new to VueJS. I'm trying to create a form with simple Save and Cancel functionality. When binding the model to form fields they get updated immediately as the inputs are changed, but I don't want that tight binding. Instead, I want to be able to save and submit when the "Save" button is pressed and revert the changes when the "Cancel" button is pressed.
What's the suggested Vue way of doing this?
It would also be ideal if we can show the server save status and indicate it on the form if the submission is failed. If you know of any examples or samples that would be hugely helpful. Thanks!
See in JSFiddle
<template>
<div id="app">
<div>
First Name:
<input type="text" v-model="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" v-model="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
<button v-if="isEditing" #click="isEditing = false">Cancel</button>
</div>
</template>
<script>
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith'
}
}
})
</script>
<style>
.view {
border-color: transparent;
background-color: initial;
color: initial
}
</style>
There's a few ways to handle this. You could create a separate component for the form, pass props to it, and then handle the editing/saving by emitting changes or if you want to keep it in a single component you could use value binding and refs, e.g.
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith'
}
},
methods: {
save() {
this.user.firstName = this.$refs['first_name'].value;
this.user.lastName = this.$refs['last_name'].value;
this.isEditing = !this.isEditing;
}
}
})
.view {
border-color: transparent;
background-color: initial;
color: initial
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div>
First Name:
<input type="text" ref="first_name" :value="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" ref="last_name" :value="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing" v-if="!isEditing">
Edit
</button>
<button #click="save" v-else-if="isEditing">
Save
</button>
<button v-if="isEditing" #click="isEditing = false">Cancel</button>
</div>
Or you could use a variable cache mechanism (with suggested edits) e.g.
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith',
}
},
mounted() {
this.cachedUser = Object.assign({}, this.user);
},
methods: {
save() {
this.cachedUser = Object.assign({}, this.user);
this.isEditing = false;
},
cancel() {
this.user = Object.assign({}, this.cachedUser);
this.isEditing = false;
}
}
})
.view {
border-color: transparent;
background-color: initial;
color: initial
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div>
First Name:
<input type="text" v-model="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" v-model="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing" v-if="!isEditing">Edit</button>
<button #click="save" v-else-if="isEditing">Save</button>
<button v-if="isEditing" #click="cancel">Cancel</button>
</div>
With any of these options you can set a status message at the start of the save method and then update it whenever you're done with the server call.

Vue view update?

Build A Single page app with vue 2 and vue-router 2
build.vue:
<style>
#build-content {
margin: 20px 20px;
}
</style>
<template>
<div id="build-content">
<h2>title</h2>
<div v-for="(buildValue, buildKey) in currentConfig">
<li v-for="(value, key) in buildValue"
is="build-item"
v-bind:buildEventId="buildKey"
v-bind:buildKey="key"
v-bind:buildValue="value"
v-on:remove="remove">
</li>
</div>
<br>
<br>
</div>
</template>
<script>
import BuildItem from './build-item.vue'
import Vue from "vue";
import qs from 'qs';
export default {
components:{ BuildItem },
data () {
return {
currentConfig: {
"1" : {
"akey" : "aValue",
"bkey" : "bValue",
"ckey" : "cValue",
},
"2" : {
"akey" : "aValue",
"bkey" : "bValue",
"ckey" : "cValue",
}
}
}
},
methods: {
remove: function (eventId, key) {
console.log(eventId + " " + key);
Vue.delete(this.currentConfig[eventId], key);
}
},
mounted: function () {
}
}
</script>
build-item.vue:
<style scoped>
.tab {
margin-right:2em
}
</style>
<template>
<div>
<br>
<span class="tab">event</span>
<Input v-model="eventId" placeholder="input..." style="width: 150px" class="tab"/>
<span class="tab">key:</span>
<Input v-model="key" placeholder="input..." style="width: 200px" class="tab"/>
<span class="tab">value:</span>
<Input v-model="value" placeholder="input..." style="width: 300px" class="tab"/>
<Button type="error" #click="remove">remove</Button>
</div>
</template>
<script>
export default {
data () {
return {
eventId: this.buildEventId,
key: this.buildKey,
value: this.buildValue,
}
},
props: {
buildEventId: {
type: String
},
buildKey: {
type: String
},
buildValue:{
type: String
}
},
methods: {
remove: function () {
this.$emit('remove', this.eventId, this.buildKey);
}
}
}
</script>
Click the first row of the list("1","akey","aValue'),but remove the third row("1","cKey","cValue") ,console.log output is correct,how to fix it?
Thanks
https://v2.vuejs.org/v2/guide/list.html#key
This default mode is efficient, but only suitable when your list
render output does not rely on child component state or temporary DOM
state (e.g. form input values).
<div v-for="(buildValue, buildKey) in currentConfig" :key="buildKey">
<li v-for="(value, key) in buildValue" :key="key"
is="build-item"
v-bind:buildEventId="buildKey"
v-bind:buildKey="key"
v-bind:buildValue="value"
v-on:remove="remove">
</li>
</div>
add :key="buildKey" and :key="key" ,Fixed the problem