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

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');
});
},

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>

Vuejs set a variable maximum for a number input

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>

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!

Why the data on the components is not updated when opening another page ? (Vue.JS 2)

I have 2 component
Component first like this : http://pastebin.com/M8Q3au0B
Because the code is a bit much, I use a pastebin
From component first, it call component two
component two is like this :
<template>
<span class="rating">
<template v-for="item in items">
<label class="radio-inline input-star" :class="{'is-selected': ((starValue >= item.value) && starValue !== null)}">
<input type="radio" class="input-rating"v-model="starValue">
</label>
</template>
</span>
</template>
<script>
export default {
props: {'star': null,'store': null,'id': null},
data() {
return {
items: [{value: 5},{value: 4},{value: 3},{value: 2},{value: 1}],
starValue: this.star
}
}
}
</script>
For example, I have two page
When in the page one, the results worked well
When I click page two, username, updated_at, comments also worked well
But, on the rating does not work. It does not update
How can I solve it?
I see list is being populated asynchronously, so it will not be available to starRating component when it first mounts. to get the value when it is populated, you may have to put a watcher on it like this:
<script>
export default {
props: {'star': null,'store': null,'id': null},
data() {
return {
items: [{value: 5},{value: 4},{value: 3},{value: 2},{value: 1}],
starValue: this.star
}
},
watch : {
star: function(newVal) {
this.starValue = newVal
}
}
}
</script>

Categories

Resources