How to restrict user to enter only 30 characters in Vuejs? - vue.js

<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>

Related

How change inputs type in VUE?

I have a input with type=radio
<div id="test">
<div class="input-group mb-3" v-for="(input, k) in inputs" :key="k">
<input type="radio" class="abc" disabled>
</div>
</div>
How can on click button i can change all inputs with class="abc" from radio to checkbox
<script>
var app = new Vue({
el: "#test",
methods: {
changeInputType(e) {
if (e.target.checked == true) {
alert('true');
} else {
alert('false');
}
},
}
});
</script>
is any simple way to do this in this changeInputType function ?
bind the input type to variable and change that variable
<template>
<div>
<input :type="inputType" />
<button #click="changeType />
</div>
</template>
<script>
export default {
data() {
return {
inputType: "text",
}
},
methods: {
changeType() {
if (this.inputType === "text") {
this.inputType = "radio"
} else {
this.inputType = "text"
}
}
}
}
</script>
Here is the solution of your answer, By clicking on button all input types will be changed from radio to checkbox.
<div id="test">
<div class="input-group mb-3" v-for="(input, k) in inputs" :key="k">
<input :type="input" class="abc" disabled>
</div>
<div class="btn btn-sm" #click="changeInputType">Change Input Type</div>
</div>
<script>
var app = new Vue({
el: "#test",
data() {
return {
inputs: ['radio', 'radio']
}
},
methods: {
changeInputType() {
this.inputs = ['checkbox', 'checkbox']
},
}
});
</script>
On butt click change the type variable to checkbox.
<script>
var app = new Vue({
el: "#test",
data() {
return {
input: 'radio'
}
},
methods: {
btnClick() {
this.input = 'checkbox'
},
}
});
</script>

Getting textbox to turn red on change

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>

Is it possible to sync Vuejs components displayed multiple times on the same page?

I have a web page that displays items. For each items there is a button (vuejs component) which allow user to toggle (add/remove) this item to his collection.
Here is the component:
<template lang="html">
<button type="button" #click="toggle" name="button" class="btn" :class="{'btn-danger': dAdded, 'btn-primary': !dAdded}">{{ dText }}</button>
</template>
<script>
export default {
props: {
added: Boolean,
text: String,
id: Number,
},
data() {
return {
dAdded: this.added,
dText: this.text,
dId: this.id
}
},
watch: {
added: function(newVal, oldVal) { // watch it
this.dAdded = this.added
},
text: function(newVal, oldVal) { // watch it
this.dText = this.text
},
id: function(newVal, oldVal) { // watch it
this.dId = this.id
}
},
methods: {
toggle: function(event) {
axios.post(route('frontend.user.profile.pop.toggle', {
pop_id: this.dId
}))
.then(response => {
this.dText = response.data.message
let success = response.data.success
this.dText = response.data.new_text
if (success) {
this.dAdded = success.attached.length
let cardPop = document.getElementById('card-pop-'+this.dId);
if(cardPop)
cardPop.classList.toggle('owned')
}
})
.catch(e => {
console.log(e)
})
}
}
}
</script>
For each item, the user can also open a modal, loaded by a click on this link:
<a href="#" data-toggle="modal" data-target="#popModal" #click="id = {{$pop->id}}">
<figure>
<img class="card-img-top" src="{{ URL::asset($pop->img_path) }}" alt="Card image cap">
</figure>
</a>
The modal is also a Vuejs component:
<template>
<section id="pop" class="h-100">
<div class="card">
<div class="container-fluid">
<div class="row">
<div class="col-12 col-lg-1 flex-column others d-none d-xl-block">
<div class="row flex-column h-100">
<div v-for="other_pop in pop.other_pops" class="col">
<a :href="route('frontend.pop.collection.detail', {collection: pop.collection.slug, pop: other_pop.slug})">
<img :src="other_pop.img_path" :alt="'{{ other_pop.name }}'" class="img-fluid">
</a>
</div>
<div class="col active order-3">
<img :src="pop.img_path" :alt="pop.name" class="img-fluid">
</div>
</div>
</div>
<div class="col-12 col-lg-6 content text-center">
<div class="row">
<div class="col-12">
<img :src="pop.img_path" :alt="pop.name" class="img-fluid">
</div>
<div class="col-6 text-right">
<toggle-pop :id="pop.id" :added="pop.in_user_collection" :text="pop.in_user_collection ? 'Supprimer' : 'Ajouter'"></toggle-pop>
</div>
<div class="col-6 text-left">
<!-- <btnaddpopwhishlist :pop_id="propid" :added="pop.in_user_whishlist" :text="pop.in_user_whishlist ? 'Supprimer' : 'Ajouter'"></btnaddpopwhishlist> -->
</div>
</div>
</div>
<div class="col-12 col-lg-5 infos">
<div class="header">
<h1 class="h-100">{{ pop.name }}</h1>
</div>
<div class="card yellow">
<div class="card p-0">
<div class="container-fluid">
<div class="row">
<div class="col-3 py-2">
</div>
<div class="col-6 py-2 bg-lightgray">
<h4>Collection:</h4>
<h3>{{ pop.collection ? pop.collection.name : '' }}</h3>
</div>
<div class="col-3 py-2 bg-lightgray text-center">
<a :href="route('frontend.index') + 'collections/' + pop.collection.slug" class="btn-round right white"></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
props: {
id: Number
},
data() {
return {
pop: {
collection: {
}
}
}
},
ready: function() {
if (this.propid != -1)
this.fetchData()
},
watch: {
id: function(newVal, oldVal) { // watch it
// console.log('Prop changed: ', newVal, ' | was: ', oldVal)
this.fetchData()
}
},
computed: {
imgSrc: function() {
if (this.pop.img_path)
return 'storage/images/pops/' + this.pop.img_path
else
return ''
}
},
methods: {
fetchData() {
axios.get(route('frontend.api.v1.pops.show', this.id))
.then(response => {
// JSON responses are automatically parsed.
// console.log(response.data.data.collection)
this.pop = response.data.data
})
.catch(e => {
this.errors.push(e)
})
// console.log('fetchData')
}
}
}
</script>
Here is my app.js script :
window.Vue = require('vue');
Vue.component('pop-modal', require('./components/PopModal.vue'));
Vue.component('toggle-pop', require('./components/TogglePop.vue'));
const app = new Vue({
el: '#app',
props: {
id: Number
}
});
I would like to sync the states of the component named toggle-pop, how can I achieve this ? One is rendered by Blade template (laravel) and the other one by pop-modal component. But they are just the same, displayed at different places.
Thanks.
You could pass a state object as a property to the toggle-pop components. They could use this property to store/modify their state. In this way you can have multiple sets of components sharing state.
Your component could become:
<template lang="html">
<button type="button" #click="toggle" name="button" class="btn" :class="{'btn-danger': sstate.added, 'btn-primary': !sstate.added}">{{ sstate.text }}</button>
</template>
<script>
export default {
props: {
sstate: {
type: Object,
default: function() {
return { added: false, text: "", id: -1 };
}
}
},
data() {
return {};
},
methods: {
toggle: function(event) {
axios.post(route('frontend.user.profile.pop.toggle', {
pop_id: this.sstate.id
}))
.then(response => {
this.sstate.text = response.data.message
let success = response.data.success
this.sstate.text = response.data.new_text
if (success) {
this.sstate.ddded = success.attached.length
let cardPop = document.getElementById('card-pop-'+this.sstate.id);
if(cardPop)
cardPop.classList.toggle('owned')
}
})
.catch(e => {
console.log(e)
})
}
};
</script>
Live demo
https://codesandbox.io/s/vq8r33o1w7
If you are 100% sure that all toggle-pop components should always have the same state, you can choose to not define data as a function. Just declare it as an object.
data: {
dAdded: this.added,
dText: this.text,
dId: this.id
}
In https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function, it mentions
a component’s data option must be a function, so that each instance
can maintain an independent copy of the returned data object
If Vue didn’t have this rule, clicking on one button would affect the
data of all other instances
Since you want to sync the data of all toggle-pop component instances, you don't have to follow the data option must be a function rule.

I got Vue errors rendering component

I have a "vue-cli webpack" like the following :
src/components/Signin.vue:
<template>
...
<form v-on:submit.prevent="userSignIn">
...
<div class="field">
<p class="control has-icons-left has-icons-right">
<input
v-validate="'required|email'"
v-bind:class="{'is-danger': errors.has('name')}"
name="email"
v-model="form.email"
class="input"
id="email"
type="email"
placeholder="Email"
>
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
<span class="icon is-small is-right">
<i class="fa fa-check"></i>
</span>
<span class="help is-danger" v-show="errors.has('email')">{{ errors.first('email') }}</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
v-validate="'required|min:5'"
v-bind:class="{'is-danger': errors.has('name')}"
name="password"
v-model="form.password"
class="input"
id="password"
type="password"
placeholder="Password"
>
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
<span class="help is-danger" v-show="errors.has('password')">{{ errors.first('password') }}</span>
</p>
</div>
<div class="field is-grouped">
<div class="control">
<button v-bind:disabled="errors.any()" class="button is-primary" type="submit" :disabled="loading">
Submit
</button>
</div>
</div>
</form>
...
</template>
<script>
...
export default {
data () {
return {
form: {
email: '',
password: '',
alert: false
}
}
},
computed: {
error () {
return this.$store.getters.getError
},
loading () {
return this.$store.getters.getLoading
}
},
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}
},
...
}
</script>
src/App.vue:
<template>
<main>
<router-view></router-view>
</main>
</template>
<style lang="sass">
#import "~bulma"
/* Your css for this file... */
</style>
src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import firebase from 'firebase'
import { store } from './store'
import VeeValidate from 'vee-validate'
import { firebaseConfig } from './config'
Vue.use(VeeValidate)
Vue.config.productionTip = false
firebase.initializeApp(firebaseConfig)
/* eslint-disable no-new */
const unsubscribe = firebase.auth()
.onAuthStateChanged((firebaseUser) => {
new Vue({
el: '#app',
router,
store,
render: h => h(App),
created () {
store.dispatch('autoSignIn', firebaseUser)
}
})
unsubscribe()
})
and I get two errors when I click the button :
Property or method "userSignIn" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
Signin.vue?d58e:24 Uncaught TypeError: _vm.userSignIn is not a
function
You've defined your methods inside your watch. Move them outside.
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}

Click-to-Edit text field with Vue

I am working on Vue js and having an issue editing a field. When I click on a field to edit it, all the editable fields become active. Here is my code.
export default {
props: ['profileHeight'],
data() {
return {
User: User,
isEditing: false,
form:{
name:'',
email: '',
},
};
},
mounted() {
},
methods: {
activateInEditMode() {
this.isEditing = true
},
deActivateInEditMode() {
this.isEditing = false
}
}
}
<span>Profile settings</span>
<p>Full name<span v-on:click="activateInEditMode" v-show="!isEditing">{{User.state.auth.name}}</span>
<span v-show="isEditing" >
<input v-model="form.name" type="text" class="form-control" >
</span>
</p>
<p>E-mail<span>{{User.state.auth.email}}</span>
<span v-show="isEditing" >
<input v-model="form.email" type="text" class="form-control" >
</span>
</p>
Try using focus and blur methods to show/hide form elements!
Hope this helps!
new Vue({
el: '#app',
data(){
return {
user : {
name: '',
email: ''
},
editField : ''
}
},
methods : {
focusField(name){
this.editField = name;
},
blurField(){
this.editField = '';
},
showField(name){
return (this.user[name] == '' || this.editField == name)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<h1>Profile settings</h1>
<label for="user-name">Full name</label>
<div class="field">
<span class="field-value" v-show="!showField('name')" #click="focusField('name')">{{user.name}}</span>
<input v-model="user.name" v-show="showField('name')" id="user-name" type="text" class="field-value form-control" #focus="focusField('name')" #blur="blurField">
</div>
<label for="user-email">Email address</label>
<div class="field">
<span class="field-value" v-show="!showField('email')" #click="focusField('email')">{{user.email}}</span>
<input v-model="user.email" v-show="showField('email')" type="email" class="field-value form-control" #focus="focusField('email')" #blur="blurField">
</div>
</div>
There are are dozens of ways to do this. I might recommend a component.
console.clear()
Vue.component("editable",{
props:["label", "value"],
template:`
<p>
{{label}}
<span #click="editing=true" v-show="!editing">
{{value}}
</span>
<span v-show="editing" >
<input :value="value"
#input="$emit('input', $event.target.value)"
#keydown.enter="editing=false"
type="text"
class="form-control" >
</span>
</p>
`,
data(){
return {
editing: false,
}
}
})
const User = {
name: 'bob',
email: 'bob#example.com'
}
new Vue({
el:"#app",
data() {
return {
form: User
};
},
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<span>Profile settings</span>
<editable label="Full name" v-model="form.name"></editable>
<editable label="E-mail" v-model="form.email"></editable>
<br>
{{form}}
</div>
I have written a component for this, I call it Click-to-Edit.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue:
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>