bootstrap-vue using colgroup to group the column with column header - vuejs2

I have to create the table as in the design shown below in the figure, using Bootstrap Vue.
Here the Male and Female is grouped by respective section, this can be done using the simple <td> and <col-group>,
there is the slot table-colgroup in bootstrap-vue documentation, but i did not found the way to use in this scenario.
I have implemented the table in the code snippets below:
Vue.config.productionTip = false
Vue.component('icons', {
template: '<a><slot></slot></a>'
})
new Vue({
el: '#app',
methods: {
addService() {
this.model.services.push({});
}
},
computed: {
sec_a_male: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_a_male) || 0)
}, 0)
},
sec_a_female: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_a_female) || 0)
}, 0)
},
sec_b_male: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_b_male) || 0)
}, 0)
},
sec_b_female: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_b_female) || 0)
}, 0)
}
},
data: {
model: {
services: []
},
fields: [{
key: "class",
label: "Class"
},
{
key: "section_a_male",
label: "Male"
},
{
key: "section_a_female",
label: "Female"
},
{
key: "section_b_male",
label: "Male"
},
{
key: "section_b_female",
label: "Male"
}
]
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://unpkg.com/vue"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-card header-tag="header" footer-tag="footer">
<template slot="header" class="mb-0">
<button type="button" class="btn btn-primary btn-sm" #click.prevent="addService">
<icons :icon="['fas', 'plus']" /> Add Items/Service</button>
</template>
<b-card-body>
<b-table responsive bordered striped hover caption-top :fields="fields" :items="model.services" caption-top>
<template slot="top-row" slot-scope="data">
<td rowspan="1"></td>
<th colspan="2" scope="colgroup">Section A</th>
<th colspan="2" scope="colgroup">Section B</th>
</template>
<template slot="class" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.class" :name="`class_${data.index}`" type="text" />
</template>
<template slot="section_a_male" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_a_male" :name="`section_a_male_${data.index}`" type="text" />
</template>
<template slot="section_a_female" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_a_female" :name="`section_a_female_${data.index}`" type="text" />
</template>
<template slot="section_b_male" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_b_male" :name="`section_b_male_${data.index}`" type="text" />
</template>
<template slot="section_b_female" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_b_female" :name="`section_b_female_${data.index}`" type="text" />
</template>
<template slot="bottom-row" slot-scope="data">
<td>Total</td>
<td>{{sec_a_male}}</td>
<td>{{sec_a_female}}</td>
<td>{{sec_b_male}}</td>
<td>{{sec_b_female}}</td>
</template>
<template slot="table-colgroup">
<col>
<col span="2">
<col span="2">
</template>
</b-table>
</b-card-body>
</b-card>
</div>
Expected Output:
Please help!

This might get you started. You can add a class bg-danger to your td elements, and you can add variants/classes to your fields array.
Vue.config.productionTip = false
Vue.component('icons', {
template: '<a><slot></slot></a>'
})
new Vue({
el: '#app',
methods: {
addService() {
this.model.services.push({});
}
},
computed: {
sec_a_male: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_a_male) || 0)
}, 0)
},
sec_a_female: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_a_female) || 0)
}, 0)
},
sec_b_male: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_b_male) || 0)
}, 0)
},
sec_b_female: function() {
return this.model.services.reduce(function(a, c) {
return a + Number((c.section_b_female) || 0)
}, 0)
}
},
data: {
model: {
services: []
},
fields: [{
key: "class",
label: "Class"
},
{
key: "section_a_male",
label: "Male",
variant: 'danger'
},
{
key: "section_a_female",
label: "Female",
variant: 'danger'
},
{
key: "section_b_male",
label: "Male",
variant: 'warning'
},
{
key: "section_b_female",
label: "Male",
variant: 'warning'
}
]
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://unpkg.com/vue"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-card header-tag="header" footer-tag="footer">
<template slot="header" class="mb-0">
<button type="button" class="btn btn-primary btn-sm" #click.prevent="addService">
<icons :icon="['fas', 'plus']" /> Add Items/Service</button>
</template>
<b-card-body>
<b-table responsive bordered hover caption-top :fields="fields" :items="model.services" foot-clone>
<template slot="class" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.class" :name="`class_${data.index}`" type="text" />
</template>
<template slot="section_a_male" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_a_male" :name="`section_a_male_${data.index}`" type="text" />
</template>
<template slot="section_a_female" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_a_female" :name="`section_a_female_${data.index}`" type="text" />
</template>
<template slot="section_b_male" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_b_male" :name="`section_b_male_${data.index}`" type="text" />
</template>
<template slot="section_b_female" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.section_b_female" :name="`section_b_female_${data.index}`" type="text" />
</template>
<template slot="bottom-row" slot-scope="data">
<td>Total</td>
<td class="bg-danger">{{sec_a_male}}</td>
<td class="bg-danger">{{sec_a_female}}</td>
<td class="bg-warning">{{sec_b_male}}</td>
<td class="bg-warning">{{sec_b_female}}</td>
</template>
</b-table>
</b-card-body>
</b-card>
</div>

This hasn't been on the boostrap-vue before, new changes has just been published on current version of boostrap-vue v2.0.0-rc.14, which support for the header row.
<b-table responsive bordered striped hover caption-top :fields="fields" :items="model.services" caption-top>
<template slot="thead-top" slot-scope="{}">
<tr>
<th></th>
<th class="text-center" colspan="2">Section A</th>
<th class="text-center" colspan="2">Section B</th>
</tr>
</template>
</b-table>
This can be done by using thead-top slot, further explained here
https://bootstrap-vue.js.org/docs/components/table#adding-additional-rows-to-the-header

This might give the alternate case:
<b-table class ="table" :fields="fields" :items="items">
<template slot="bottom-row" slot-scope="{}">
<th colspan="5" class="text-center">No Data</th>
</template>
</b-table>
get header table from :fields
default row in bottom with text "No Data" align center. Post condition :items is null.
Check table output below:

Related

vue2 + element ui - how to setting dynamic v-model

Let's make the string(categoryV3) an array with ','
and create array length.
and icon-add click add ..
but v-model not working.. and i don't know add .........
I am not good at speaking English.
Please give me your opinion.
<template>
<el-table
:data="tableData"
>
<el-table-column type="selection" width="55" align="center"> </el-table-column>
<el-table-column props="category" label="category" show-overflow-tooltip>
<template slot-scope="{row}">
<div v-for="(item, index) in generateArray(row.categoryV3)" :key="index">
<el-select class="filter-item select1" filterable v-model="item[index]" placeholder="-">
<el-option
v-for="item in options"
:key="item.value"
:label="`${item.value}. ${item.label}`"
:value="item.value"
/>
</el-select>
<span class="tmp-icon icon-add"><i class="el-icon-circle-plus-outline"></i></span>
<span class="tmp-icon icon-remove" v-show="generateArray(row.categoryV3).length > 1"
><i class="el-icon-remove-outline"></i
></span>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [{
categoryV3:"option1,option2"
}, {
categoryV3:""
}],
options: [{
value: 'Option1',
label: 'Option1'
}, {
value: 'Option2',
label: 'Option2'
}]
};
},
methods: {
generateArray(val) {
return val.split(',');
},
}
};
</script>
data add 'categoryV3: []'
<div v-for="(item, index) in categoryV3[scope.$index]" :key="index">
<el-select class="filter-item select1" filterable v-model="categoryV3[scope.$index][index]" placeholder="-">
<el-option
v-for="item in relationCode"
:key="item.code_value"
:label="`${item.code_value}. ${item.code_name}`"
:value="item.code_value"
/>
</el-select>
<span class="tmp-icon icon-add" #click="addModel(categoryV3[scope.$index], '')"
><i class="el-icon-circle-plus-outline"></i
></span>
<span
class="tmp-icon icon-remove"
#click="removeModel(categoryV3[scope.$index], index)"
v-show="categoryV3[scope.$index].length > 1"
><i class="el-icon-remove-outline"></i
></span>
</div>

Autofill input fields

I need following to work. I have an input field (in my Code number1) and I need to autofill (not autocomplete) the other input fields (in my Code autofill1 + autofill2).
I'm working with Bootstrap Vue (Bootstrap 4.6 and Vue.js 2).
Here is my code:
<template>
<div class="m-2 mt-3">
<table class="table table-striped mt-2">
<tbody>
<h5 class="ml-1">Informations</h5>
<tr>
<div class="row">
<div class="col-md-6 m-1">
<div class="mt-2">number1</div>
<b-form-input class="form-control" placeholder="1234567" id="number1" />
</div>
</div>
<div class="row">
<div class="col-md-5 ml-1 mr-1">
<div class="mt-2">autofill1</div>
<b-form-select id="autofill1"> </b-form-select>
</div>
<div class="col-md-5 ml-1 mr-1">
<div class="mt-2">autofill2</div>
<b-form-select id="autofill2"> </b-form-select>
</div>
<div class="row">
<div class="col-md-3 mt-2 ml-1">
<b-button variant="success" type="submit"><b-icon icon="check-circle-fill"></b-icon> Save Changes</b-button>
</div>
</div>
</div>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
};
</script>
<style scoped>
</style>
So my goal is to load some data (first guess was an json file) into my script and after I write a matching number (also possible that its a text) in input field number1 the other two fields will be autofilled.
Thanks in advance for helping me out!
Structure json data:
[
{ "number": 1234567, "autofill1": "Hello", "autofill2": "Goodbye" },
{ "number": 9876543, "autofill1": "Ciao", "autofill2": "Arrivederci" },
{ "number": 1346795, "autofill1": "Hallo", "autofill2": "Tschuess" }
]
Something like this would work: Example
<template>
<div>
<input type="number" placeholder="1234567" v-model.number="number1" />
<input type="text" :value="getAutofill.autofill1" />
<input type="text" :value="getAutofill.autofill2" />
</div>
</template>
<script>
export default {
computed: {
getAutofill(){
if (!this.data.find(item => item.number == this.number1)) return ["",""]
return this.data.find(item => item.number == this.number1)
}
},
data() {
return {
number1: undefined,
data: [
{ "number": 1234567, "autofill1": "Hello", "autofill2": "Goodbye" },
{ "number": 9876543, "autofill1": "Ciao", "autofill2": "Arrivederci" },
{ "number": 1346795, "autofill1": "Hallo", "autofill2": "Tschuess" }
]
}
}
};
</script>
you need to add some things to the data and than watch for the variable number1 to change...
<template>
<div class="m-2 mt-3">
<table class="table table-striped mt-2">
<tbody>
<h5 class="ml-1">Informations</h5>
<tr>
<div class="row">
<div class="col-md-6 m-1">
<div class="mt-2">number1</div>
<b-form-input class="form-control" placeholder="1234567" id="number1" v-model="number1" />
</div>
</div>
<div class="row">
<div class="col-md-5 ml-1 mr-1">
<div class="mt-2">autofill1</div>
<b-form-input class="form-control" id="autofill1" v-model="autofill1" />
</div>
<div class="col-md-5 ml-1 mr-1">
<div class="mt-2">autofill2</div>
<b-form-input class="form-control" id="autofill2" v-model="autofill2" />
</div>
<div class="row">
<div class="col-md-3 mt-2 ml-1">
<b-button variant="success" type="submit"><b-icon icon="check-circle-fill"></b-icon> Save Changes</b-button>
</div>
</div>
</div>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data(){
return {
number1: "",
autofill1: "",
autofill2: "",
jsonDATA: {...}
}
},
methods: {
findData(key) {
for(let i=0; i < this.jsonDATA.length; i++){
if(this.jsonDATA[i]['number'] === key) return element[i]
}
// add somthing when no data in array...
}
},
watch: {
number1(){
try {
let tmp = this.findData(this.number1)
this.autofill1 = tmp['autofill1']
this.autofill2 = tmp['autofill2']
}
}
}
};
</script>
<style scoped>
</style>
I havent tested this but this should work... This is not complete, you need to add some behaviour what to do when no data is found...

how to reuse a first template inside a second div

Here I have written two steps one is "step===1" and other one is "step===2". So here i wants to reuse step1 template to step2. That means we have step one template and at step1 we click on next button so at next step i want to show the same template like reusing..
<div v-if="step === 1">
<h1>Step One</h1>
<h4>Address</h4>
<label>Address</label>
<input type="text" v-model="address">
<label>City</label>
<input type="text" v-model="city">
<label>State</label>
<input type="text" v-model="state">
<button #click.prevent="next()">Next</button>
</div>
<div v-if="step === 2">
reuse step===1 template here
</div>
vue.js
export default {
data() {
return {
address: null,
city: null,
state: null
}
},
methods:{
prev() {
this.step--;
},
next() {
this.step++;
},
}
</script>
new Vue({
el: '#app',
data() {
return {
stepTotal: 2,
step: 1,
address: null,
city: null,
state: null
}
},
methods: {
prev() {
this.step--;
},
next() {
this.step++;
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="index of stepTotal">
<div v-if="step === index" :key="index">
<h1>Step {{index}}</h1>
<h4>Address</h4>
<label>Address</label>
<input type="text" v-model="address">
<label>City</label>
<input type="text" v-model="city">
<label>State</label>
<input type="text" v-model="state">
<br>
<button v-if="index != 1" #click.prevent="prev()">Prev</button>
<button v-if="index != stepTotal" #click.prevent="next()">Next</button>
</div>
</template>
</div>
I'd probably separate the form for each step into its own component. So for instance:
AddressForm.vue
<template>
<div>
<label>Address</label>
<input type="text" v-model="localValue.address" />
<label>City</label>
<input type="text" v-model="localValue.city" />
<label>State</label>
<input type="text" v-model="localValue.state" />
</div>
</template>
<script>
export default {
props: {
value: Object,
},
computed: {
localValue: {
get() { return this.value; },
set(value) { this.$emit("input", value); },
},
},
};
</script>
And then just reuse this component for each step:
<div v-if="step === 1">
<h1>Step One</h1>
<h4>Address</h4>
<AddressForm v-model="step1address />
<button #click.prevent="next()">Next</button>
</div>
<div v-if="step === 2">
<h1>Step Two</h1>
<h4>Address 2</h4>
<AddressForm v-model="step2address />
<button #click.prevent="next()">Next</button>
</div>
Here's a running sample of the above:
https://codesandbox.io/s/tender-antonelli-2n4vb

Using same .vue component twice in same parent component - how to differentiate

I want to use the same component twice from a parent component; all that was stopping me was the use of an ID for a dropdown in the subcomponents! So I currently have duplicated the subcomponent.
My parent component:
<template>
<div id="compare-page">
<div class="row">
<div class="col-md-12">
<div class="row campaign-overview">
<div class="col-md-4">
<h2>{{ campaign.name }}</h2>
<strong>Published</strong>
<strong>Starts: {{ campaign.start_date }} - Ends: {{ campaign.end_date }}</strong>
</div>
<div class="col-md-4 text-right">
<select name="daterange" id="" #change="onChangeDaterange($event)">
<option v-for="(daterange, index) in dateranges" v-bind:key="daterange.index" :value="index" :data-start="daterange.start" :data-end="daterange.end" :selected="daterange.optionSelected == 1">{{ index }}</option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<left-device ref="leftDevice"></left-device>
</div>
<div class="col-md-6">
<right-device ref="rightDevice"></right-device>
</div>
</div>
</div>
</template>
<script>
import left from './compared_device_component_left.vue';
import right from './compared_device_component_right.vue';
export default {
components: {
'left-device': left,
'right-device': right,
},
mounted() {
this.fetchCampaignData().then(result => {
});
},
data() {
return {
campaign_id: this.$route.params.campaign_id,
campaign: [],
dateranges: [],
leftDevice: '',
rightDevice: ''
}
},
methods: {
fetchCampaignData() {
return this.$http.get('/api/portal/campaign/' + this.campaign_id + '/campaign?' + this.axiosParams)
.then(function(data){
return data.json();
}).then(function(data){
this.campaign = data.campaign;
this.dateranges = data.dateranges;
});
},
onChangeDaterange(event) {
this.$router.push('?range=' + event.target.value);
this.$refs.leftDevice.onChangeDaterange(event);
this.$refs.rightDevice.onChangeDaterange(event);
},
},
computed: {
axiosParams() {
const params = new URLSearchParams();
if(this.$route.query.range) params.append('range', this.$route.query.range);
return params;
}
},
}
</script>
<style lang="scss">
</style>
One of my subcomponents
<template>
<div>
<div class="row device-overview">
<div class="col-md-4">
<select name="devices" id="deviceLeft" #change="onChangeDevice($event)">
<option v-for="(device, index) in devices" v-bind:key="device.index" :value="index" :data-device_id="device.device_id" :data-address="device.address" :selected="device.optionSelected == 1">{{ device.device_id }} | {{ device.address }}</option>
</select>
</div>
</div>
<div class="row totals">
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Total Interactions</h5>
<p class="card-text">{{ total_interactions }}</p>
</div>
</div>
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Average Interactions Per Day</h5>
<p class="card-text">{{ average_daily_interactions }}</p>
</div>
</div>
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Total Average Dwell Time</h5>
<p class="card-text">{{ average_dwell_time }}s</p>
</div>
</div>
</div>
<div class="row interactions">
<div class="col-md-12">
<h5>Total Interactions Per Product</h5>
<table class="table table-condensed table-hover">
<tbody>
<tr>
<td class="">Tag</td>
<td class="">Mapped Product</td>
<td class="">Lifts</td>
</tr>
<tr v-for="interaction in table_product_interactions">
<td class="">{{ interaction.tag }}</td>
<td class="">{{ interaction.mapping }}</td>
<td>{{ interaction.tag_count }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12">
<h5>Total Interactions Per Day</h5>
<div class="chart" ref="chartdaily"></div>
</div>
</div>
<div class="row interactions">
<div class="col-md-8">
<h5>Total Interactions Per Hour</h5>
<div class="chart" ref="charthourly"></div>
</div>
</div>
</div>
</template>
<script>
import * as am4core from "#amcharts/amcharts4/core";
import * as am4charts from "#amcharts/amcharts4/charts";
import am4themes_animated from "#amcharts/amcharts4/themes/animated";
am4core.useTheme(am4themes_animated);
export default {
mounted() {
this.fetchData(null).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
},
data() {
return {
campaign_id: this.$route.params.campaign_id,
campaign: [],
dateranges: [],
device: [],
devices: [],
total_interactions: '',
average_daily_interactions: '',
average_dwell_time: '',
interaction_type: '',
chart_daily_interactions: [],
chart_hourly_interactions: [],
table_product_interactions: []
}
},
methods: {
fetchData(deviceID) {
// Use JS to get by ID!
var device = document.getElementById("deviceLeft");
// ... call to API
},
chartTotalPerDay() {
// ...
},
chartTotalPerHour() {
// ...
},
onChangeDaterange(event) {
// Use JS to get by ID!
var device = document.getElementById("deviceLeft");
// st cookie
document.cookie = "deviceLeft="+device.value;
this.fetchData(device.value).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
},
onChangeDevice(event) {
// st cookie
document.cookie = "deviceLeft="+event.target.value;
this.fetchData(event.target.value).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
}
},
created() {
},
computed: {
axiosParams() {
const params = new URLSearchParams();
if(this.$route.query.range) params.append('range', this.$route.query.range);
return params;
}
},
filters: {
},
directives: {
},
}
</script>
<style lang="scss">
.interactions {
h5 {
text-align: center;
}
}
.chart {
min-height: 550px;
}
</style>
I am currently using
var device = document.getElementById("deviceLeft");
...in my duplicated left/right subcomponents (this is 'left' and I also have 'right').
Should I use a 'ref' for my devices dropdown, so that each instance of the subcomponent is looking at the devices dropdown relative to that instance? Currently if I merge these into one the devices dropdown ID is the same for both instances and therefore I get incorrect ID in one of the instances!
Any helpful pointers would be much appreciated.
Thanks,
K...

Vue Js How to calculate the value on the table and display the sum on the footer

I wanted to create simple invoice using bootstrap table and Vue Js.
Basically, What i wanted is shown in the image below:
I have tried as in the code below, but i am confused on two things,
How should i
1) Calculate the total cost and show that as the footer summary.
2) Multiply rate and qnty and display on the corresponding input box on cost.
new Vue({
el: '#app',
methods: {
addService() {
this.model.services.push({});
}
},
data: {
model: {
services: []
},
fields: [{
key: "rate",
label: "Rate"
},
{
key: "qnty",
label: "Qnty"
},
{
key: "cost",
label: "Cost"
}
]
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://unpkg.com/vue"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-card header-tag="header" footer-tag="footer">
<template slot="header" class="mb-0">
<button type="button" class="btn btn-primary btn-sm" #click.prevent="addService">
<icons :icon="['fas', 'plus']" /> Add Items/Service</button>
</template>
<b-card-body>
<b-table responsive bordered striped hover caption-top :fields="fields" :items="model.services" foot-clone>
<template slot="rate" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.rate" :name="`rate_${data.index}`" type="text" />
</template>
<template slot="qnty" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.qnty" :name="`qnty_${data.index}`" type="text" />
</template>
<template slot="cost" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.cost" :name="`cost_${data.index}`" type="text" />
</template>
</b-table>
</b-card-body>
</b-card>
</div>
The way i wanted is easily achieved by using normal td and tr, with computed function.
But i am confused with how to implement using Bootstrap-vue.
Please help!
Here's a quick way, that calculates the item cost in place
<b-form-input :value="(data.item.rate * data.item.qnty) || 0" type="text" />
Improvements can be made here to update the item total in the item, by using a watch t update the data.
the total, however is done using a computed value that uses reduce to find the total
computed: {
total: function() {
return this.model.services.reduce(function(a, c){return a + Number((c.rate*c.qnty) || 0)}, 0)
}
},
here is the complete code:
Vue.config.productionTip = false
Vue.component('icons', {
template: '<a><slot></slot></a>'
})
new Vue({
el: '#app',
methods: {
addService() {
this.model.services.push({});
}
},
computed: {
total: function() {
return this.model.services.reduce(function(a, c){return a + Number((c.rate*c.qnty) || 0)}, 0)
}
},
data: {
model: {
services: []
},
fields: [{
key: "rate",
label: "Rate"
},
{
key: "qnty",
label: "Qnty"
},
{
key: "cost",
label: "Cost"
}
]
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://unpkg.com/vue"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-card header-tag="header" footer-tag="footer">
<template slot="header" class="mb-0">
<button type="button" class="btn btn-primary btn-sm" #click.prevent="addService">
<icons :icon="['fas', 'plus']" /> Add Items/Service</button>
</template>
<b-card-body>
<b-table responsive bordered striped hover caption-top :fields="fields" :items="model.services" foot-clone>
<template slot="rate" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.rate" :name="`rate_${data.index}`" type="text" />
</template>
<template slot="qnty" slot-scope="data">
<b-form-input size="sm" class="form-control" v-model="data.item.qnty" :name="`qnty_${data.index}`" type="text" />
</template>
<template slot="cost" slot-scope="data">
<b-form-input size="sm" class="form-control" :value="(data.item.rate * data.item.qnty) || 0" :name="`cost_${data.index}`" type="text" />
</template>
<template slot="bottom-row" slot-scope="data">
<td/><td>Total</td>
<td>{{total}}</td>
</template>
</b-table>
</b-card-body>
</b-card>
</div>