When new bars value are set, the data is not updated. Please check below;
I'm planning to do dynamic bars and buttons. Once the buttons are clicked, the new values will be updated to bars values.
Here is the link
HTML
<h1><span>Vue.js</span> Progress Bar</h1>
<div id="app">
<div v-for="(index, bar) in bars" class="shell">
<div class="bar" :style="{ width: bar + '%' }" .>
<span>{{ bar }}%</span>
<input type="radio" id="radio-bar" value="{{ index }}" v-model="picked" v-on:change="set(index)">
</div>
</div>
<span v-for="(index, button) in buttons">
<button value="{{ button }}" #click.prevent="makeProgress(button)">{{ button }}</button>
</span>
<br>
<p>Selected Bar: {{ picked }}</p>
<p>Button Value: {{ buttonVal }}</p>
</div>
JS
var dataURL = 'http://pb-api.herokuapp.com/bars';
var vm = new Vue({
el: "#app",
data: {
maxColor: "#F22613",
bars: [],
buttons: [],
limit: 0,
selectedBar: 0,
buttonVal: 0
},
methods: {
fetchData: function(params) {
this.$http.get(dataURL, function(data) {
this.$set('bars', data.bars);
this.$set('buttons', data.buttons);
this.$set('limit', data.limit);
console.log(params);
});
},
set: function(index) {
this.selectedBar = index;
},
makeProgress: function(button) {
var self = this;
self.buttonVal += button;
this.reachMax();
Vue.set(this.bars, 0, 55);
},
reachMax() {
if(this.buttonVal >= this.limit){
alert('Reached Limit' + this.limit)
}
}
},
created: function() {
this.fetchData();
}
});
Related
In this below code click on the reset button to uncheck all radio buttons but I have to click on the reset button to check the default radio button
<div id="app">
<div v-for="(val, key) in list">
<input type="radio" name="radio" :value="val" v-model="selected" :id="val" #click="uncheck( val )"><label :for="val">{{ val }}</label>
</div>
<button #click="uncheckAll">Reset</button>
</div>
var app = new Vue({
el: '#app',
data: {
list: ['one', 'two', 'three'],
selected: 'two',
previous: null,
},
methods: {
uncheck: function(val) {
if (val == this.previous) {
this.selected = false;
}
this.previous = this.selected;
},
uncheckAll: function() {
this.selected = false;
},
}
})
It's working fine but I need to reset the selected value, for that, I can not find any solution there.
Please take a look at following snippet (just set default value on reset):
var app = new Vue({
el: '#app',
data() {
return {
list: [ 'one', 'two', 'three' ],
selected: 'two',
}
},
methods : {
uncheck(){
this.selected = 'two';
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(val, key) in list">
<input type="radio" name="radio" :value="val" v-model="selected" :id="val">
<label :for="val">{{ val }}</label>
</div>
<button #click="uncheck">Reset</button>
{{ selected }}
</div>
I have a const array of objects which set to the data property on created(), I am trying to update the object properties as the user entered the form, the Console print shows the value updated, but the DOM is changing. DOM is updated when the key value is changed too but I do not want to change the key value
Data Array:
const invoices = [
{
number: "BBRFL",
client: "Ellison Daugherty",
amount: 8800,
status: "Paid",
},
{
number: "TYUBK",
client: "Myrna Vinson",
amount: 4097,
status: "Paid",
},
{
number: "IRUBR",
client: "Mcmillan Warner",
amount: 6466,
status: "Draft",
},
];
Here is the app.
const app = new Vue({
el: "#app",
components: {
"create-invoice": CreateInvoice,
"invoice-item": InvoiceItem,
},
data() {
return {
invoices: invoices,
status: null,
};
},
created: function () {
const invoices = localStorage.getItem("invoices");
if (invoices) {
this.invoices = JSON.parse(invoices);
}
},
computed: {
count: function () {
return this.filteredInvoices.length;
},
filteredInvoices: function () {
if (this.status) {
return this.invoices.filter((invoice) => invoice.status == this.status);
}
return this.invoices;
},
},
methods: {
saveInvoice(invoice) {
this.invoices.push(invoice);
},
invoiceUpdate(invoice) {
const index = this.invoices.findIndex((i) => i.number === invoice.number);
// this.invoices.splice(index, 1, invoice);
Vue.set(this.invoices[index], "client", invoice.client);
Vue.set(this.invoices[index], "amount", invoice.amount);
Vue.set(this.invoices[index], "status", invoice.status);
},
invoiceDelete(number) {
const index = this.invoices.findIndex((i) => i.number === number);
this.invoices.splice(index, 1);
},
},
watch: {
invoices: {
deep: true,
handler: function (invoices) {
localStorage.setItem("invoices", JSON.stringify(invoices));
},
},
},
template: `
<div>
<div class="row">
<div class="col-md-4 text-left">
<h1>Invoices</h1>
<p class="text-dark">There are {{count}} Total Invoices</p>
</div>
<div class="col-md-4 text-start" style="margin-top: 10px">
<label for="status">Filter</label>
<select name="filter" id="status" class="form-control" v-model="status">
<option value="Draft">Draft</option>
<option value="Paid">Paid</option>
<option value="Pending">Pending</option>
</select>
</div>
<div class="col-md-4 text-right" style="margin-top: 10px">
<button class="btn btn-primary mt-4" id="createInvoice">
New Invoice
</button>
</div>
</div>
<div class="col-md-12">
<div class="row mt-2 mb-2">
<div
class="invoice col-md-12"
>
<invoice-item
v-for="(n, index) in filteredInvoices"
:key="n.number"
:initial-client="n.client"
:initial-number="n.number"
:initial-amount="n.amount"
:initial-status="n.status"
#update-invoice="invoiceUpdate"
#delete-invoice="invoiceDelete"
></invoice-item>
</div>
<div class="text-center" v-if="filteredInvoices.length === 0"><p>No invoice found</p></div>
</div>
</div>
<create-invoice #saveInvoice="saveInvoice" ></create-invoice>
</div>
</div>
`,
});
I had tried, this.$set, Vue.set, Direct assigning to property, assingment using splice function, but none of working. It only works with the change of value of key in for loop. Which I do not want to update.
Jsfiddle
Any Help? Thanks in advance.
I am a newbie to Vue.
I am working on multiple stopwatch using Vue.
I'm stuck, becuase my component is updating the values on all instances of the components, instead of just in one.
This is what I tried:
<div id="app">
<user-name></user-name>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<script type='text/x-template' id="test-template">
<div>
<div class="column" v-for="(item, index) in items" :key="index">
<div class="ui segment">
<h3 class="ui blue header">{{parentTitle}}</h3>
<h2 class="ui greenheader">{{item.name}}</h2>
<div class="column">
<p class="ui huge header">
{{ hours }} :
{{ minutes | zeroPad }} :
{{ seconds | zeroPad }} :
{{ milliSeconds | zeroPad(3) }}</p>
<button class="ui secondary button" #click="startTimer"
:disabled="isRunning">START</button>
<button class="ui button" #click="pushTime" :disabled="!isRunning">LAP</button>
<button class="ui button" #click="stopTimer" :disabled="!isRunning">STOP</button>
<button class="ui basic button" #click="clearAll">CLEAR</button><br><br>
<ul class="ui bulleted list" v-if="times.length">
<li class="item" v-for="item in times">
{{ item.hours }} :
{{ item.minutes | zeroPad }} :
{{ item.seconds | zeroPad }} :
{{ item.milliSeconds | zeroPad(3) }}
</li>
</ul>
<br><br>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('user-name', {
data() {
return {
parentTitle: "Employee Names",
test: "welcome",
times: [],
animateFrame: 0,
nowTime: 0,
diffTime: 0,
startTime: 0,
isRunning: false,
items: [{
id: 1,
name: 'Employee 1'
},
{
id: 2,
name: 'Employee 2'
}
],
count: 0
}
},
template: '#test-template',
methods: {
// 現在時刻から引数に渡した数値を startTime に代入
setSubtractStartTime: function (time) {
var time = typeof time !== 'undefined' ? time : 0;
this.startTime = Math.floor(performance.now() - time);
},
// タイマーをスタートさせる
startTimer: function () {
// loop()内で this の値が変更されるので退避
var vm = this;
//console.log(this);
//alert(timer0.innerText);
vm.setSubtractStartTime(vm.diffTime);
// ループ処理
(function loop() {
vm.nowTime = Math.floor(performance.now());
vm.diffTime = vm.nowTime - vm.startTime;
vm.animateFrame = requestAnimationFrame(loop);
}());
vm.isRunning = true;
//alert(innerText);
},
// タイマーを停止させる
stopTimer: function () {
this.isRunning = false;
cancelAnimationFrame(this.animateFrame);
},
// 計測中の時間を配列に追加
pushTime: function () {
this.times.push({
hours: this.hours,
minutes: this.minutes,
seconds: this.seconds,
milliSeconds: this.milliSeconds
});
},
// 初期化
clearAll: function () {
this.startTime = 0;
this.nowTime = 0;
this.diffTime = 0;
this.times = [];
this.stopTimer();
this.animateFrame = 0;
}
},
computed: {
// 時間を計算
hours: function () {
return Math.floor(this.diffTime / 1000 / 60 / 60);
},
// 分数を計算 (60分になったら0分に戻る)
minutes: function () {
return Math.floor(this.diffTime / 1000 / 60) % 60;
},
// 秒数を計算 (60秒になったら0秒に戻る)
seconds: function () {
return Math.floor(this.diffTime / 1000) % 60;
},
// ミリ数を計算 (1000ミリ秒になったら0ミリ秒に戻る)
milliSeconds: function () {
return Math.floor(this.diffTime % 1000);
}
},
filters: {
// ゼロ埋めフィルタ 引数に桁数を入力する
// ※ String.prototype.padStart() は IEじゃ使えない
zeroPad: function (value, num) {
var num = typeof num !== 'undefined' ? num : 2;
return value.toString().padStart(num, "0");
}
}
});
new Vue({
el: "#app",
});
</script>
Here is a working JSFiddle sample here
Any help highly appreciated.
Here is a solution to your problem jsfiddle
In your code you are mixing the data form your vue instance, with the data of your component.
Instead of having 1 component you can add your component multiple times with the v-for
This is your vue instance + data:
new Vue({
el: "#app",
data() {
return {
people: [{
id: 1,
name: 'Employee 1'
},
{
id: 2,
name: 'Employee 2'
}
]
}
}
});
The solution is to pass the data of the person to a component via props (here called item), and render this component as many time as needed in the array. This way, each component is a independent "instance".
<user-name v-for="(person, index) in people" :key="person.id" :item="person"></user-name>
Vue.component('user-name', {
props:['item'],
.....
I am trying to make a form which contains an input group section, in this group, there are one select box and multiple checkboxes. Checkboxes are populated based on the select box selection. There is also an add and remove button to generate and remove input group. The select box is used with v-model to filtered the checkboxes. But when I generate a new input group and make changes, all checkboxes are changed.
I want them to be isolated. How can I achieve?
Here is my Template.
<template>
<form #submit.prevent="onSubmit">
<div v-for="(variationProduct, index) in variationProducts" :key="variationProduct.id">
<div class="from-group mb-4">
<label class="col-form-label"><b>Categories :</b></label>
<select class="form-control mr-2" ref="categories" v-model="category">
<option value="0">Please select category...</option>
<option v-for="category in categories" :key="category.id" :value="category.id">
{{ category.name }}
</option>
</select>
<div v-if="hasError">
<validation-errors v-if="errors['categories.'+index]" :errors="errors">
{{ errors['categories.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="form-group mb-4">
<label class="col-form-lablel"><b>Variation Products :</b></label>
<div class="row">
<div v-for="filteredVariationProduct in filteredVariationProducts" :key="filteredVariationProduct.id">
<div class="col-12">
<input :id="filteredVariationProduct.id" :value="filteredVariationProduct.id" type="checkbox" ref="variationProducts">
<label :for="filteredVariationProduct.id">{{ filteredVariationProduct.name }}</label>
</div>
</div>
</div>
<div v-if="hasError">
<validation-errors v-if="errors['variationProducts.'+index]" :errors="errors">
{{ errors['variationProducts.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="float-right">
<button #click.prevent="add" class="btn btn-success">Add</button>
<button #click.prevent="remove(index)" class="btn btn-danger">Remove</button>
</div>
<br>
<br>
<hr>
</div>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</template>
Here is my JS.
<script>
import ValidationErrors from './ValidationErrors.vue';
export default {
components: {
'validation-errors': ValidationErrors,
},
data () {
return {
variationProducts: [],
categories: [
{ id: 1, name: 'Technology'},
{ id: 2, name: 'Business'},
{ id: 3, name: 'Marketing'},
{ id: 4, name: 'Development'},
{ id: 5, name: 'Engineering'},
],
category: 0,
activeVariationProducts: [],
count: 1,
errors: {},
hasError: false,
}
},
methods: {
add: function() {
this.count++;
this.errors = {};
this.hasError = false;
this.variationProducts.push({ id: this.count });
},
remove: function (index) {
if ( this.variationProducts.length > 0 && index > -1) {
this.variationProducts.splice(index, 1);
} else {
alert('Must have at least one input!')
}
},
onSubmit: function() {
console.log(this.$refs.variationProducts.value);
},
generateVariationProducts: function(num) {
for(let i = 1; i <= num; i++) {
let activeVariationProduct = {
id: i,
name: 'Product '+ i,
category_id: i
};
this.activeVariationProducts.push(activeVariationProduct);
}
},
},
computed : {
filteredVariationProducts: function () {
let categoryId = parseInt(this.category);
if (categoryId !== 0) {
let filteredVariationProducts = this.activeVariationProducts.filter((variationProduct) => {
return variationProduct.category_id === categoryId;
});
return filteredVariationProducts;
} else {
let filteredVariationProducts = this.activeVariationProducts;
return filteredVariationProducts;
}
}
},
created () {
this.variationProducts.push({ id: this.count });
this.generateVariationProducts(10);
},
}
</script>
Here is a sample code. This code Shows how you can use multiple Checkboxes that is generated dynamically and how to make them isolated -
new Vue({
el : "#app",
data : {
Items : ["One", "Two", "Three"],
newCheckbox : "",
SelectedItems : {
'One' : "",
'Two' : "",
'Three' : "",
},
},
methods:{
add:function(){
Vue.set(this.SelectedItems, this.newCheckbox, false);
this.Items.push(this.newCheckbox);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js" type="text/javascript"></script>
<div id="app">
<div>
<label v-for="(item, index) in Items">
<input type="checkbox" v-bind:key="index" v-model="SelectedItems[item]"> {{ item }}
</label>
</div>
<div>
<input type="text" v-model="newCheckbox">
<button #click="add">Add</button>
</div>
<div>
Output : {{ SelectedItems }}
</div>
</div>
You can dynamically add new checkbox and still they are isolated
I need to be able to switch between an input field and a label. When the button "Add Location" is clicked (which create a new div), the input field must be visible. But when the div "Expandable" is maximized it must be hidden and the label visible instead!
The input field should only be visible right after the mentioned button is clicked, else the label has to take its place. What is the best way to achieve this? I was thinking about using some sort of toggle since I am using that in other places.
The label and the input field is placed in the div class "switch".
You can also see the code in this jsFiddle!
Html
<div id="lotsOfDivs">
<addingdivs></addingdivs>
</div>
Vue
var gate = 0;
Vue.component('addingdivs', {
template: `
<div>
<div id="header">
<button class="addDiv" type="button" #click="createDiv">ADD LOCATION</button>
</div>
<div class="parent" v-for="div in divs" :style=" div.height ? { 'height': div.height }: null">
<div class="big" v-if="div.expanded" :key="'expanded' + div.id">
<div class="switch">
<input type="text" v-if="inputFieldInfo">
<label class="propertyLabel" v-else>
<div class="firstChild">
<button class="done" #click="increaseLimit">INCREASE</button>
</div>
<div class="secondChild">
<button class="done" #click="expand(div)">EXPAND</button>
</div>
</div>
<div class="small" v-else :key="'collapsed' + div.id">
<button class="done" #click="expand(div)">EXPAND</button>
</div>
</div>
</div>
`,
data: function() {
return {
gate: gate,
height: "",
count: 0,
locationsArr: ["one", "two", "three"],
divs: [],
InputFieldInfo: false
}
},
methods: {
expand: function(div) {
if (div.expanded) {
div.expanded = false
this.height = ''
} else {
div.expanded = true
this.height = '7vh'
}
},
createDiv: function() {
if (this.count <= gate) { // Here you can decide how many divs that will be generated
// this.count++;
this.divs.push({
id: this.count,
expanded: true,
inputFieldInfo: true,
height: '',
});
this.count++
}},
increaseLimit: function() {
// Here you can increase the number of divs that it's possible to generate
gate++;
}
}
});
new Vue({
el: '#lotsOfDivs',
});
The template had a few compilation errors:
The <label> needs a closing tag (and text content to be useful)
The <div class="big"> needs a closing tag
The v-if was bound to inputFieldInfo, but that variable was declared as InputFieldInfo (note the uppercase I), but based on your behavior description, this field should be unique per location container, so a single data property like this wouldn't work (if I understood your description correctly).
Each location container should have a variable to contain the location name (e.g., locationName) and another variable to contain the show/hide Boolean for the <input> and <label> (i.e., inputFieldInfo):
createDiv: function() {
this.divs.push({
// ...
inputFieldInfo: true,
locationName: ''
});
}
Then, we could bind div.inputFieldInfo and div.locationName to the <input>. We bind to v-model so that the user's text is automatically reflected to the div.locationName variable:
<input v-if="div.inputFieldInfo" v-model="div.locationName">
The <label>'s content should be div.locationName so that it contains the text from the <input> when shown:
<label class="propertyLabel" v-else>{{div.locationName}}</label>
To switch the <input> with the <label> when the expand-button is clicked, we update expand() to set div.inputFieldInfo to false but only when div.locationName is not empty (this gives the user a chance to revisit/re-expand the container to fill in the location later if needed):
expand: function(div) {
if (div.expanded) {
div.expanded = false
if (div.locationName) {
div.inputFieldInfo = false
}
// ...
updated jsfiddle
You had some missing closing tags and an error with InputFieldInfo, it should have a lowercase i.
var gate = 0;
Vue.component('addingdivs', {
template: `
<div>
<div id="header">
<button class="addDiv" type="button" #click="createDiv">ADD LOCATION</button>
</div>
<div class="parent" v-for="div in divs" :style=" div.height ? { 'height': div.height }: null">
<div class="big" v-if="div.expanded" :key="'expanded' + div.id">
<div class="switch">
<input type="text" v-if="inputFieldInfo">
<label class="propertyLabel" v-else>Label</label>
<div class="firstChild">
<button class="done" #click="increaseLimit">INCREASE</button>
</div>
<div class="secondChild">
<button class="done" #click="expand(div)">EXPAND</button>
</div>
</div>
</div>
<div class="small" v-else :key="'collapsed' + div.id">
<button class="done" #click="expand(div)">EXPAND</button>
</div>
</div>
</div>
`,
data: function() {
return {
gate: gate,
height: "",
count: 0,
locationsArr: ["one", "two", "three"],
divs: [],
inputFieldInfo: true
}
},
methods: {
expand: function(div) {
this.inputFieldInfo = false
if (div.expanded) {
div.expanded = false
this.height = ''
} else {
div.expanded = true
this.height = '7vh'
}
},
createDiv: function() {
this.inputFieldInfo = true
if (this.count <= gate) { // Here you can decide how many divs that will be generated
// this.count++;
this.divs.push({
id: this.count,
expanded: true,
inputFieldInfo: true,
height: '',
});
this.count++
}
},
increaseLimit: function() {
// Here you can increase the number of divs that it's possible to generate
gate++;
}
}
});
new Vue({
el: '#lotsOfDivs',
});
You just basically toggle the inputFieldInfo data, whenever each button is pressed.
You can do that by using toggle variable like this
Vue.component('addingdivs', {
template: `
<div>
<div>
<input type="text" v-if="takeinput">
<label v-if="!takeinput">
<button #click="toggleInput()">
</div>
</div>
`,
data: function() {
return {
takeinput:true,
}
},
methods: {
toggleInput: function(){
let vm = this;
vm.takeinput = ( vm.takeinput == true) ? false : true
}
}
});
new Vue({
el: '#lotsOfDivs',
});
In this example, we are just toggeling value of takeinput on click , so according the value either label or input will be showed.
This is very basic exmpale. But you can extend it as your need