Vuejs set a variable maximum for a number input - javascript

I have the following vue js script:
<template>
<div>
<div v-if='cart.length > 0'>
<h1>Your Cart</h1>
<template>
<fieldset v-for='product in cart'>
<image :src='product.image'
<h4>{{product.name}}</h4>
<input type='number' :max='quantCheck'/>
<h5>{{product.price}}</h5>
</fieldset>
</template>
</div>
<div v-else><h1>Your Cart Is Empty</h1></div>
<br />
<h5>Subtotal: </h5>
<h5>Shipping: Free for a limited time!</h5>
<h2>Total: </h2>
</div>
</template>
<script>
const apiURL = 'http://localhost:3000';
import axios from 'axios';
export default {
data() {
return {
cart: [
{
id:"56461",
name:"lilly",
quantity: 2,
price: 30.10
}, {
id:"1253",
name:"wild",
quantity: 1,
price: 31.10
}
]
}
},
methods: {
let quantCheck = this.cart.product.quantity
}
}
</script>
I haven't been able to find a good way to make something like this work.
The quantity is variable, and I guess maybe I could make a function that checks the number after each input and pops an error when it goes above but that's not quite the goal.
Anyway sorry if this is a stupid question but thanks for your help in advance!

You can use HTML Form validation for input (type="number"):
<input type='number' :max='product.quantity'/>
If the input is greater than max value then it will show error on Submit the form

I believe that what you want to do is limit the number of items in <input type='number' :max='quantCheck'/> based on the quantity property of the item in your cart. If this is the case, there's a few things that can be improved in your component.
First, you are binding :max="quantityCheck" to your input. looking at your component, you have defined quantityCheck in the methods option.
methods: {
let quantCheck = this.cart.product.quantity
}
This is incorrect, there's no method declaration. You'll need to limit the number of characters using the quantity property directly:
new Vue({
el: '#app',
template: `
<div>
<fieldset v-for='product in cart'>
<h4>{{product.name}}</h4>
<input type='number' :max="product.quantity"/>
<h5>{{product.price}}</h5>
</fieldset>
</div>`,
data: () => ({
cart: [
{
id: "56461",
name: "lilly",
quantity: 2,
price: 30.10
},
{
id: "1253",
name: "wild",
quantity: 1,
price: 31.10
}
]
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
The above works, to test it, enter a value higher than the quantity and the input should highlight on blur
If you want better validation, I would suggest using Vee-Validate for a simple way to validate your inputs.
Using VeeValidate
Vue.use(VeeValidate);
new Vue({
el: '#app',
template: `
<div>
<fieldset v-for='product in cart'>
<h4>{{product.name}}</h4>
<input v-validate="'max_value:'+product.quantity" :name="product.name" type="number">
<span v-if="errors.first(product.name)">Quantity cannot be more than {{product.quantity}}</span>
<h5>{{product.price}}</h5>
</fieldset>
</div>`,
data: () => ({
cart: [{
id: "56461",
name: "lilly",
quantity: 2,
price: 30.10
},
{
id: "1253",
name: "wild",
quantity: 1,
price: 31.10
}
]
})
});
<script src="https://cdn.jsdelivr.net/npm/vee-validate#latest/dist/vee-validate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Related

Dinamically modify data in Vue instance based on input element

I'm a beginner with Vue and JS and I'm struggling to update the Vue instance's data attribute based on user input. This is what I have as the template for the Vue component (located inside taskTemplate variable):
<div>
<input type="text" placeholder="Insert your task" v-model="desc"/>
<p>{{ desc }}</p>
</div>
The Vue component is defined as follows:
Vue.component("task-item", {
props: {
id: Number,
desc: String,
},
template: taskTemplate
});
And this is populated in the HTML as follows:
<div id="task-list">
<task-item
v-for="item in taskList"
v-bind="item"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
Where the task list is created with the Vue instance:
var app = new Vue({
el: "#task-list",
data: {
taskList: [
{ id: 1, desc: "" },
{ id: 2, desc: "" },
]
},
methods: {
newTask() {
this.taskList.push({ id: this.taskList.length + 1, desc: "" })
}
}
});
My problem is that after updating the input element in the webpage, the component's property gets updated, but if I type in the console app.taskList[0].desc it still returns an empty string.
The end goal is to send the data the user has introduced in an API call, so if I can access Vue components instead of the taskList within the data property it is still ok. I would like to know the best practices here as well.
Props shouldn't be used in a two-way binding. Instead, bind their value to input :value, and emit any changes to the parent component.
<div>
<input
type="text"
placeholder="Insert your task"
:value="desc"
#input="$emit('update:desc', $event.target.value)"
/>
<p>{{ desc }}</p>
</div>
In the parent component then you have to listen to update event and update the source value:
<div id="task-list">
<task-item
v-for="item in taskList"
:key="item.id" // do not forget about key
v-bind="item"
#update:desc="item.desc = $event"
// or you can use the sync modifier to listen to update events for all item's props.
// v-bind.sync="item"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
Try to use :value and #input instead v-model :
Vue.component('task-item', {
template: `
<div>
<input type="text"
placeholder="Insert your task"
:value="desc"
#input="$emit('update', $event.target.value)"/>
<p>{{ desc }}</p>
</div>
`,
props: {
id: Number,
desc: String,
},
//template: taskTemplate
})
new Vue({
el: '#demo',
data: {
taskList: [
{ id: 1, desc: "" },
{ id: 2, desc: "" },
]
},
methods: {
newTask() {
this.taskList.push({ id: this.taskList.length + 1, desc: "" })
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div id="task-list">
<task-item
v-for="(item, index) in taskList"
:key="index"
v-bind="item"
#update="item.desc = $event"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
</div>

How create multiple store filter in VueJS?

i'm new to VueJS and this is my first big project. In this project I have 2 filters for my elements, one is a search bar and the other is based on checkboxs. As you can see in my code, I have a computed propertie with 2 filters, the second filter is supposed to display the products with the same brand bu it doesn't work and I don't know why. If anybody have an idea it would be cool ;)
<div class="col-xs-12">
<input type="text" v-model="search" placeholder="Rechercher" class="search">
</div>
<div class="row">
+ filtres
<transition name="fade">
<div v-if="show" class="filter__container">
<ul>
<li>
<input type="checkbox" v-model="getBrand" v-on:click="filtered" v-bind:value="brand" />
<label for="apple">Apple</label>
</li>
<li>
<input type="checkbox" v-model="getBrand" v-on:click="filtered" v-bind:value="brand" />
<label for="samsung">Samsung</label>
</li>
</ul>
</div>
</transition>
</div>
<div class="row between-xs no-margin grid">
<div v-for="product in filtered" class="containers no-padding no-margin item">
<router-link to="/items">
<div #click="getProduct(product)">
<img :src="product.img" :alt="product.alt" class="img">
<div class="content">
<h3>{{ product.title }}</h3>
<p>{{ product.description }}</p>
<p>{{ product.brand }}</p>
</div>
</div>
</router-link>
</div>
</div>
<script>
import app from '../App'
import {mapActions} from 'vuex';
export default {
components: {
app
},
data() {
return {
show: false,
search: '',
brand: ['apple','samsung'],
getBrand:[],
}
},
computed: {
products() {
return this.$store.state.products;
},
filtered: function () {
return this.products.filter((product) => {
return product.title.toLowerCase().match(this.search.toLowerCase())
return product.brand.match(this.getBrand.includes(brand))
})
},
},
methods: {
...mapActions([
'currentProduct',
]),
getProduct(product) {
this.currentProduct(product);
}
},
};
</script>
export const store = new Vuex.Store({
state: {
products: [{
img: '../icons/img.png',
alt: 'logo',
title: 'Title',
description: 'Description',
brand: 'Apple'
},
{
img: '../icons/img.png',
alt: 'logo',
title: 'Title2',
description: 'Description2',
brand: 'Apple'
},
{
img: '../icons/img.png',
alt: 'logo',
title: 'Title3',
description: 'Description3'
brand: 'Samsung'
},
{
img: '../icons/img.png',
alt: 'logo',
title: 'Title4',
description: 'Description4'
brand: 'Samsung'
}
],
currentProduct: {},
},
getters: {
getProduct: state => state.currentProduct,
},
mutations: {
CURRENT_PRODUCT: (state, product) => {
state.currentProduct = product;
}
},
actions: {
currentProduct: (context, product) => {
context.commit('CURRENT_PRODUCT', product);
}
}
})
You can't return twice from the same function. Either chain the conditions using && or chain in another call to filter.
You're also misusing match. The argument needs to be a RegExp or something that can safely be converted to a RegExp. You can see the problem in the console if you type in a character like [ that has a special meaning in a RegExp. Perhaps you meant includes?
The second condition also seems to be incorrect. Not entirely clear what that combination of match and includes is trying to achieve but I think you're looking for something like this:
return this.products.filter((product) => {
return product.title.toLowerCase().includes(this.search.toLowerCase()) &&
this.getBrand.includes(product.brand)
})
It is worth noting that while both conditions are using a method called includes they are two different methods, one on a string and the other on an array.
This also seems to be wrong:
v-bind:value="brand"
brand is an array of strings and you aren't looping over them with a v-for. Change it to value="Apple" and value="Samsung" instead, ensuring the case matches the data.
I also suggest removing v-on:click="filtered". Not sure what that's trying to do but it seems to be treating a computed property as a click listener.

Dynamic number of input fields

I'm working in a installment payment Component, and I need a dynamic list of Form Inputs, according the number of installment payments the User selects (from 1 to 12).
I don't know how I could get the values of the future Form Inputs.
When I have just one Form Input, I use 'v-model' to capture the value of the input field that already exists.
But with multiple instances of a component inside a loop, I can't figure it out.
// The User choose 6
// The value 6 (Number) goes to a property inside data() with v-model
data () {
return {
numberOfFields: 6
}
}
Then to a v-for
<template v-for="n in numberOfFields">
<input type="text" v-model="????">
</template>
I don't want to create every possibility like:
data(){
return {
inputField1: '',
inputField2: '',
inputField3: '',
// up to inputField12
}
}
I want to capture the value of the input field only if the input exists, but without creating every possible option beforehand.
You can use:
<template v-for="n in numberOfFields">
<input type="text" v-model="$data['inputField' + n]">
</template>
Demo:
new Vue({
el: '#app',
data() {
return {
numberOfFields: 6,
inputField1: '11',
inputField2: '22',
inputField3: '33',
inputField4: '44',
inputField5: '55',
inputField6: '66',
// up to inputField12
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template v-for="n in numberOfFields">
<input type="text" v-model="$data['inputField' + n]"> {{ $data['inputField' + n] }}<br>
</template>
</div>
But this is unusual. Generally, we create another object and use it (instead of the data root).
Example using another object, called fields:
new Vue({
el: '#app',
data() {
return {
numberOfFields: 6,
fields: {
inputField1: '11',
inputField2: '22',
inputField3: '33',
inputField4: '44',
inputField5: '55',
inputField6: '66',
// up to inputField12
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<b>Using numberOfFields:</b><br>
<template v-for="n in numberOfFields">
<input type="text" v-model="fields['inputField' + n]"> {{ fields['inputField' + n] }}
</template>
<br><hr><br>
<b>Using (val, key):</b><br>
<template v-for="(val, key) in fields">
<input type="text" v-model="fields[key]"> {{ fields[key] }}
</template>
</div>

Vue.js how to delete component v-for values

I am learning Vue, so I created radio button component, but I am struggling with how can one delete these values. My current solution deletes actual values, but selection is still displayed.
This is the component
<template id="fradio">
<div>
<div class="field is-horizontal">
<div class="field-label" v-bind:class = "{ 'required' : required }">
<label
class = "label"
>{{label}}
</label>
</div>
<div class="field-body">
<div>
<div class="field is-narrow">
<p class="control" v-for="val in values">
<label class = "radio">
<input
type="radio"
v-bind:name = "name"
v-bind:id = "name"
#click = "updateValue(val)"
>
<span>{{val[valueLabel]}}</span>
<span v-if="!valueLabel">{{val}}</span>
</label>
<label class="radio">
<button class="delete is-small" #click="removeValue"></button>
</label>
<slot></slot>
</p>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
label: {
type: String,
required: true,
},
inputclass: {
type: String,
},
required: {
type: Boolean,
default: false,
},
valueLabel:{
type: String,
},
returnValue:{
type: String,
},
values:{},
name:'',
},
data() {
return {
};
},
methods: {
updateValue: function (value) {
var selectedValue;
(!this.returnValue) ? selectedValue = value : selectedValue = value[this.returnValue];
this.$emit('input', selectedValue)
},
removeValue: function() {
this.$emit('input',null);
},
},
}
</script>
It should be easy, but I need someone to point out the obvious...
Update:
I just realized that you may be more focused on the data not dynamically updating, which means that your issue might be that the data in the parent component is not being updated. Most of your data is being passed down as props, so I'd need to see how the event is being fired in the parent component in order to help diagnose what's wrong. Based on the code you provided, it looks like your removeValue() function is emitting an event but I don't see any code that actually removes the value.
I would check the parent component to make sure that it is removing the child component and that should fix your problem!
Initial Answer:
Generally, when removing an item from a v-for list, you need to know the index of the item and use the Array.splice in order to modify the list to remove it.
Here's a generic example off the top of my head.
<template>
<ul>
<li v-for="(fruit, index) in fruits"
#click="removeItem(index)">
{{ fruit }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Clementines']
}
},
methods: {
removeItem(index) {
this.fruits.splice(index, 1)
}
}
}
</script>
Let me know if you have any questions!

How to add star when clicked on vue.js 2?

My view is like this :
<div class="col-md-8">
...
<star-rating :value="3"></star-rating>
...
</div>
My component star-rating is like this :
<template>
<span class="rating">
<template v-for="item in items">
<label class="radio-inline input-star">
<input type="radio" class="input-rating" name="input-rating" v-bind:value="item.value" #click="rate(item.value)">
</label>
</template>
</span>
</template>
<script>
export default{
data(){
return{
items: [
{value: 5},
{value: 4},
{value: 3},
{value: 2},
{value: 1}
]
}
},
methods:{
rate: function (star) {
this.$http.post(window.BaseUrl + '/star', {star: star}).then(function (response) {
console.log('submitted');
});
},
}
}
</script>
When the system executed, I want display star = 3 and when the star clicked, it display the star
I'm still confused when use vue.js
How can I do it?
One approach would be to add a star variable to your data with a default value of 3 and then follow this example in the vue.js docs on form input bindings.
It will be something like this:
<input v-for="item in items" type="radio" v-model="star" v-bind:value="item.value">
<span>Picked: {{ star }}</span> //for testing that the var changes.
Finally, you would want to send the star to your backend:
methods:{
rate: function () {
var star = this.star
this.$http.post(window.BaseUrl + '/star', {star: star}).then(function (response) {
console.log('submitted');
});
},

Categories

Resources