adding component on add button - vue.js

i am a absolute beginner in vuejs,i have a feature of adding dynamic input fields on click of a button it will keep on adding rows and keeping in mind the counter should be incrementing also so that i can validate on backend, this is my code so far
<div id="settlement_container" class="container-fluid mt-4">
<div class="card rounded-0 shadow-lg">
<div class="card-body p-0">
<div class="card-header px-2">
<div class="row wow fadeIn">
<div class="col-5">
<h3>Add Store Status</h3>
</div>
</div>
</div>
<form class="custom-form-group" action="{{url('stores/addStoreStatusDB')}}" method="POST">
<div class="form-group col-6">
<label for="exampleInputEmail1">Tax</label>
<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="tax" placeholder="Tax" required>
</div>
<div class="display-inline">
<div class="form-group col-md-6">
<button #click="addstatus" class="btn btn-primary">Add Rows</button>
</div>
</div>
<div class="display-inline">
<div class="form-group col-md-6">
<button type="submit" class="btn btn-primary">Update Tax</button>
</div>
</div>
<dynamic-rows/>
</form>
</div>
</div>
</div>
{{-- Main layout --}}
#push('script')
<script src="{{ asset('js/app_vue.js') }}" ></script>
<script>
Vue.component('dynamic-rows',{
//accept data inside template
props:['counter'],
//accept data inside template
template:"<label for='exampleInputEmail1'>counter</label>"
});
const app = new Vue({
el: '#settlement_container',
data: {
counter:0
},
component:['dynamic-rows'],
methods:{
addstatus:function(e){
appendDiv=""
e.preventDefault();
alert("inside");
}
}
});
</script>
now i can do this in jquery in 5 minutes , but as i am beginner in vuejs i cant developer the sense of it of how to do it, i have a component and i want to repeat the component every time the button is clicked,
here is the fiddle! fiddle

OK, so a lot going on here and I think it may be easier to break down some of the points in isolation for you to play with and learn.
To add inputs, I think it makes more sense to have the values being in an array. Using Vue, you can iterate through that array to let each array element have its own <input/> while also simply adding another array element to add a new input:
<template>
<div>
<div v-for="(tax, index) in taxes" :key="index">
<input v-model="taxes[index]" />
</div>
<button type="number" #click="add">Add</button>
<p>Count: {{taxes.length}}</p>
</div>
</template>
<script>
export default {
data(): {
return {
taxes: [0]
}
},
methods: {
add() {
this.taxes.push(0);
}
}
});
</script>
Now with regards to the counter, I don't know what you mean validate on the backend. You could add a watcher on the taxes array and process changes there? Watchers are used sparingly, with computed properties being much preferred, but they may make sense if you need to be sending data to the backend instead of into the DOM.
The counter prop you registered in your code is not really going to work for the pattern I showed. Generally, props are for parent components to pass data to child components. The preferred pattern when sending data from child to parent is to use $emit. Read more here.

Related

Vue.js and Bootstrap Modal - Invoking JS

Trying to implement the bootstrap-datepicker in conjunction with Vue, and not quite sure why my solution won't work.
I am using the code example at https://eonasdan.github.io/bootstrap-datetimepicker/
I added a v-on:click on the INPUT that then ties to a method.
TEMPLATE CODE:
<div class="container">
<div class="row">
<div class='col-sm-6'>
<div class="form-group">
<div class='input-group date' id='datetimepicker1'>
<input v-on:click="displayCal" type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</div>
</div>
VUE
import jquery from 'jquery';
....
methods: {
displayCal: function () {
jquery('#datetimepicker1').datetimepicker();
}
}
Will that help with what I'm trying to do?
Not exactly, I misunderstood and thought that the datepicker is a bootstrap component. Unfortunately it is not, and so while vue implementation of bootstrap could be helpful, it would leave you still with quite a bit to implement yourself.
Option 1
Use a Vue component that has the functionality out of the box
const app = new Vue({
el: '#app',
components: {
vuejsDatepicker
}
})
<div id="app">
<vuejs-datepicker></vuejs-datepicker>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuejs-datepicker"></script>
Demo: https://codesandbox.io/s/mpklq49wp
If that does what you need then vuejs-datepicker component may be better for you to implement.
Option 2
Get jquery and Vue to talk
This is not ideal, because the two libraries will compete for DOM control. Vue mounts and unmounts of DOM elements are data driven. That means if your data changes, your DOM may replace an element that jquery may be relying on. SO you may be able to get it to work, but based on all the other stuff going on in the app, you may find some odd things happening, that said, here is a way you can do it...
<div id="app">
<div class="container">
<p>
TIMEDATE: {{timedate}}
</p>
<div class="row">
<div class='col-sm-6'>
<input type='text' class="form-control" id='datetimepicker4' />
</div>
</div>
</div>
</div>
const app = new Vue({
el: '#app',
data(){
return{
timedate: new Date(),
}
},
methods:{
onUpdate(e){
this.timedate=e.date;
}
},
mounted() {
const dp = $('#datetimepicker4')
dp.datetimepicker({
defaultDate: this.timedate,
});
dp.on('dp.change', this.onUpdate )
}
})
demo: https://jsfiddle.net/fjk6cLo8/7/
This code will allow you to add the datetime picker with a preset date. Upon change of the date, using the datetime picker, the data will be updated in the vue component (using dp.change listener)
The datetime picker is mounted once right after the component mounts, and the <input type='text' class="form-control" id='datetimepicker4' /> element is available.

How to make iterations using v-for directive on nested array (array in array)

I am new to Vue and I came upon a situation that I would like to have some advice on.
In my js file I have some arrays that contain some data that I would like to insert in a table:
const d1=[{col1:"aaa1",col2:"bbb1",col3:"ccc1",col4:"ddd1",col5:"eee1"},
{col1:"aaa2",col2:"bbb2",col3:"ccc2",col4:"ddd2",col5:"eee2"}]
const d2=[{col1:"fff1",col2:"ggg1",col3:"hhh1",col4:"iii1",col5:"jjj1"},
{col1:"aaa2",col2:"bbb2",col3:"ccc2",col4:"ddd2",col5:"eee2"}]
then I saved the two arrays in another variable called availableData
const availableData=[d1,d2];
my vue instance as follows:
new Vue({
el: '#c1',
data: {
availableData,
}
});
In my HTML I am trying to add a for loop(v-for) in my row div so the row can display each of data in my availableData variable, but I am having some problems trying to pass d1 to the first row and d2 to the second,
<div id="c1" class="columns">
// ...some code
<div class="row" v-for="data in availableData">
<div class="cell">
{{data.col1}}
</div>
<div class="cell">
{{data.col2}}
</div>
<div class="cell">
{{data.col3}}
</div>
<div class="cell">
{{data.col4}}
</div>
<div class="cell">
{{data.col5}}
</div>
</div>
</div>
Of course, the v-for statement is not correct since I am trying to iterate through the availableData array, if I were to write
v-for="data in availableData[i]"
then is there a way to pass a varaible like i to achieve iteration, or is this method not a plausible way to conduct?
You have several solutions to do what you want.
Solution # 1 :
You can alter the availableData to display all data like you want. You have just to flat you array like this : const availableData=[...d1, ...d2];
With such a code your availableData variable will have :
const availableData = [{col1:"aaa1",col2:"bbb1",col3:"ccc1",col4:"ddd1",col5:"eee1"},
{col1:"aaa2",col2:"bbb2",col3:"ccc2",col4:"ddd2",col5:"eee2"},
{col1:"fff1",col2:"ggg1",col3:"hhh1",col4:"iii1",col5:"jjj1"},
{col1:"aaa2",col2:"bbb2",col3:"ccc2",col4:"ddd2",col5:"eee2"}]
Solution # 2
You can make a double iteration in your template :
<div class="data" v-for="data in availableData">
<div class="row" v-for="row in data">
<div class="cell">
{{row.col1}}
</div>
<div class="cell">
{{row.col2}}
</div>
<div class="cell">
{{row.col3}}
</div>
<div class="cell">
{{row.col4}}
</div>
<div class="cell">
{{row.col5}}
</div>
</div>
</div>

how to emit a value with a click event in vue.js

I am following Vuejs documentation and trying to emit a value with click event.
The code follows:
Vue Template:
<div id="app">
<div class="container">
<div class="col-lg-offset-4 col-lg-4">
<sidebar v-on:incrementBtn="increment += $event"></sidebar>
<p>{{ increment }}</p>
</div>
</div>
</div>
Vue instances:
Vue.component('sidebar',{
template:`
<div>
<button class="btn btn-info" v-on:click="$emit('incrementBtn', 1)">Increment on click</button>
</div>
`,
});
new Vue ({
el:'#app',
data:{
increment:0
}
});
Check Vue Custom Event, Vue always recommends using kebab-case for event names.
And as above Vue guide said:
Unlike components and props, event names don’t provide any automatic
case transformation. Instead, the name of an emitted event must
exactly match the name used to listen to that event.
And
Additionally, v-on event listeners inside DOM templates will be
automatically transformed to lowercase (due to HTML’s
case-insensitivity), so v-on:myEvent would become v-on:myevent – making myEvent impossible to listen to.
Vue.component('sidebar',{
template:`
<div>
<button class="btn btn-info" v-on:click="$emit('increment-btn', 1)">Increment on click</button>
</div>
`
})
new Vue ({
el:'#app',
data () {
return {
increment:0
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div class="container">
<div class="col-lg-offset-4 col-lg-4">
<sidebar v-on:increment-btn="increment += $event"></sidebar>
<p>{{ increment }}</p>
</div>
</div>
</div>

changing dinamically class and id on vue components

i'm a bit new to vue, and maybe i'm trying to do something that is not doable.
i have this skeleton:
<div class="row"> <!--FIRST ROW-->
<skills-list></skills-list>
</div>
<div class="row"> <!--SECOND ROW-->
<skills-list></skills-list>
</div>
<div class="row"> <!--LAST ROW-->
<skills-list></skills-list>
</div>
now i have three row, each of them contain a vue custom component, this is my app:
Vue.component('skills-list', {
template: `
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
<div class="text-center">
<i :class="skills[0].class"></i>
</div>
<div class="progress-container">
<div class="progress" :id="skills[0].id"></div>
</div>
</div>
`,
data: function(){
return {
skills : [
{'class':'icon-html5-alt skills_icon','id':'#progress-html'},
{'class':'icon-css3-alt skills_icon','id':'#progress-css'},
{'class':'icon-javascript-alt','id':'#progress-js'}
]
}
}
});
new Vue({
el: '#cv_app',
});
my custom component template has two dinamic value, the class and the id, that are listed in the skills array.
i want to fill those value for every template i insert in the html code, i mean: replace in some way :class="skills[0].class with :class="skills[i].class iterating over the skills array.
Hope i explain my self clear, is it possible?

Vue 2 Laravel 5.3 Infinte Update Loop

There is no console log error but anything that I put in updated() hook [in the current code getCartItems/] will be rendered infinitely for some reasons that I do not know. even I set it as alert('hi') and it alert it infinitely. So I suspect something makes the project keeps updating values or something but I do not know where. Can anyone give me a suggestion to check where the problem is?
Cart-dropdown.vue
<template>
<div class="row">
<div class="col-md-12">
<div class="row cart-dropdown-row" v-for="cart in carts">
<div class="col-md-3">
<div class="cart-dropdown-img-wrapper">
<img class="d-flex align-self-center cart-dropdown-img" :src="cart.product_choice.img1" alt="Generic placeholder image">
</div>
</div>
<div class="col-md-6 cart-dropdown-info-wrapper">
<h6 class="cart-dropdown-info">{{cart.product.name}}</h6>
</div>
<div class="col-md-3 cart-dropdown-qty-wrapper">
<div class="cart-dropdown-qty">x{{cart.qty}}</div>
</div>
</div>
</div>
<div class="row">
</div>
<div class="row cart-dropdown-checkout-wrapper">
<button type="button" class="btn btn-success btn-sm cart-dropdown-checkout" #click.prevent="goCheckout()">Check Out</button>
</div>
</div>
</template>
<script>
export default {
data(){
return{
carts:{},
}
},
props:[
],
mounted(){
this.getCartItems()
},
updated(){
this.getCartItems()
},
methods:{
getCartItems(){
var vm = this
vm.$http.get('/getCartItems/').then((response)=>{
vm.carts = response.data
});
},
goCheckout(){
window.location.href = 'http://localhost:8000/cart'
}
},
computed:{
}
}
</script>
You are updating the vue instance data variable carts in the updated hooks and as docs says: updated hook is called after a data change causes the virtual DOM to be re-rendered and patched. So you are in a infinite loop: you change the vue data it updates the the DOM and call the updated block which again change the data.
This is also mention in the docs:
you can perform DOM-dependent operations in this hook. However, in most cases you should avoid changing state in this hook, because it may lead to an infinite update loop.
You can see this circle in the below vue instance lifecycle diagram.