Dynamically button and page generation in VueJS - javascript

I have a JSON file that feeds data from the backend and into a table (Bootstrap-Vue built in). In that table I want to have VueJS create a button that would be generated under a specific condition and if clicked would then generate a new page with additional details.
Here is a sample of the JSON output:
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
So if "has_violations" is true, then the button would be generated that onclick would request additional data from the backend via JSON and then generate a new page based on that data.
So sort of the vanilla js equivalent of event.target.closest('tr').dataset.userId
But how can I do this in VueJS?
Edited in Table Code (Per Request):
<template>
<b-container fluid>
<!--Search Controls-->
<b-row>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Filter" class="mb-0">
<b-input-group>
<b-form-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" #click="filter = ''">Clear</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort" class="mb-0">
<b-input-group>
<b-form-select v-model="sortBy" :options="sortOptions">
<option slot="first" :value="null">-- none --</option>
</b-form-select>
<b-form-select :disabled="!sortBy" v-model="sortDesc" slot="append">
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort direction" class="mb-0">
<b-input-group>
<b-form-select v-model="sortDirection" slot="append">
<option value="asc">Asc</option>
<option value="desc">Desc</option>
<option value="last">Last</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Per page" class="mb-0">
<b-form-select :options="pageOptions" v-model="perPage" />
</b-form-group>
</b-col>
</b-row>
<!--Search Controls-->
<!-- Main table element -->
<b-table show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
#filtered="onFiltered"
>
<template slot="driverName" slot-scope="row">{{row.value.driver_first_name}} {{row.value.driver_last_name}}</template>
<template slot="truckNumber" slot-scope="row">{{row.value.driver_truck}}</template>
<template slot="truckNumber" slot-scope="row">{{row.value.driver_trailer}}</template>
<template slot="status" slot-scope="row">{{row.value.driver_status}}</template>
<template slot="violations" slot-scope="row">{{row.value?'Yes':'No'}}</template>
<template slot="actions" slot-scope="row">
<router-link :to="{name: 'driver_violations_list'}">
<b-button id="driverLogs">View Driver Logs</b-button>
</router-link>
</template>
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0" />
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
data () {
return {
items: items,
fields: [
{ key: 'driver_name', label: 'Driver Name', sortable: true, sortDirection: 'desc' },
{ key: 'truck_number', label: 'Truck Number', sortable: true, 'class': 'text-center' },
{ key: 'trailer_number', label: 'Trailer Number', sortable: true, 'class': 'text-center' },
{ key: 'has_violations', label: 'Violations' },
{ key: 'driver_status', label: 'Status' },
{ key: 'actions', label: 'Actions' }
],
currentPage: 1,
perPage: 5,
totalRows: items.length,
pageOptions: [ 5, 10, 15 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null,
}
},
computed: {
sortOptions () {
return this.fields
.filter(f => f.sortable)
.map(f => { return { text: f.label, value: f.key } })
}
},
methods: {
onFiltered (filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
}
//Get JSON
getDriverStatus: function () {
const url = 'driver_status_data.json'
axios.get(url, {
dataType: 'json',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode: 'no-cors',
credentials: 'include'
})
.then(function (response) {
console.log(JSON.stringify(response.data))
this.courses = JSON.stringify(response.data)
})
.catch(function (error) {
console.log(error)
})
}
}
}
</script>

Use the table slots as documented here to conditionally render the button.
Here's an example:
new Vue({
el: '#app',
computed: {
driverName () {
return this.driver ? `${this.driver.driver_name.driver_first_name} ${this.driver.driver_name.driver_last_name}` : 'N / A'
}
},
methods: {
showViolations (driver) {
this.driver = driver
this.loading = true
// Simulate an async api call with setInterval and setTimeout
let interval = setInterval(() => {
this.progress += 10
}, 250)
setTimeout(() => {
this.loading = false
this.progress = 0
clearInterval(interval)
}, 2500)
}
},
data () {
return {
progress: 0,
driver: null,
loading: false,
violations: [
'Violation 1',
'Violation 2',
'Violation 3'
],
fields: {
driver_id: {
label: 'ID',
sortable: true
},
first_name: {
key: 'driver_name.driver_first_name',
label: 'First Name',
sortable: true
},
last_name: {
key: 'driver_name.driver_last_name',
label: 'Last Name',
sortable: true
},
driver_truck: {
label: 'Truck',
sortable: true
},
driver_trailer: {
label: 'Trailer',
sortable: true
},
driver_status: {
label: 'Status',
sortable: true
},
has_violations: {
label: 'Violations',
sortable: true
}
},
items: [
{
driver_id:2,
driver_name: {
driver_first_name: 'Bob',
driver_last_name: 'Dole'
},
driver_truck: 58,
driver_trailer: 37,
driver_status: 'Sleeping',
has_violations: true
},
{
driver_id:3,
driver_name: {
driver_first_name: 'John',
driver_last_name: 'Lennon'
},
driver_truck: 69,
driver_trailer: 34,
driver_status: 'Deep Sleeping',
has_violations: false
}
]
}
}
})
<!-- Add this to <head> -->
<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://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- Add this after vue.js -->
<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-table striped hover :fields="fields" :items="items">
<template slot="has_violations" slot-scope="data">
<template v-if="data.item.has_violations">
<b-button size="md" variant="primary" v-b-modal.violations #click="showViolations(data.item)">
See Violations
</b-button>
</template>
</template>
</b-table>
<b-modal id="violations" :title="`Violations By: ${driverName}`">
<b-progress v-show="loading" :value="progress" :max="100" animated></b-progress>
<p v-if="!loading" class="my-4" v-for="violation in violations" :key="violation">{{ violation }}</p>
</b-modal>
</div>

Related

Why is my Modal not popping up after boolean change?

I am having this issue with a project using Vue 2.6.12. The goal is to open "add-metric" modal when the user closes "close-metric" modal with an option or multi-options selected. These 2 modals are separate components that are a child to the parent component. I am able to close the "close-metric" modal, pass the data into the parent component, flip a boolean called "add" to "true" on the parent component to signal "add-metric" modal to open. However,the "add-metric" modal doesn't open with the :if="add" even when the boolean is true. No error pops up at all.
Here is the "Close-metric" modal Code which builds the request to send over to the Parent Component.
addProjectMetric(projectMetricRequest) {
this.errors = [];
let request = {
projectMetricId: this.$helpers.generateUUID(),
projectMetricGroupId: this.projectMetric.projectMetricGroupId,
projectId: this.projectId,
type: projectMetricRequest.resolutionReason,
name: projectMetricRequest.name,
status: "Open",
tags: projectMetricRequest.tags,
description: projectMetricRequest.description,
dateCreated: projectMetricRequest.dateResolved,
dateIdentified: projectMetricRequest.dateResolved,
createdBy: [projectMetricRequest.createdBy],
assignedToUsers: this.projectMetric.assignedToUsers,
identifiedByRole: projectMetricRequest.identifiedByRole,
timeSaved: projectMetricRequest.timeSaved,
costSaved: projectMetricRequest.costSaved,
participants: projectMetricRequest.participants,
};
this.addProjectMetricFromParent(request);
//request = this.applyOverrides(request);
},
addProjectMetricFromParent(projectMetric) {
this.$emit('add-Resolution-Metric', projectMetric)
this.cancel();
},
cancel() {
this.reset();
this.hide();
},
reset() {
this.errors = [];
// this.form = this.newForm();
this.form = null;
},
hide() {
this.showCloseModal = false;
},
Here is the parent page html with the two components I am closing and trying to open
<CloseProjectMetricModal
v-if="item.action === 'Close'"
:ref="`close-${slugify(addNewText)}-modal`"
:projectId="$route.params.id"
:title="`Close "${limitText(currentMetric)}"`"
mode="Close"
:projectMetric="currentMetric"
:possibleAssignees="item.possibleAssignees"
:addTypes="item.addTypes"
:meetingId="$route.params.meetingId"
#add-Resolution-Metric="openAddModal($event)"
#submit="closeProjectMetricSubmit(item)"
/>
<addProjectMetricModal
v-if="add"
:ref="`add-project-metric-reason-modal`"
:projectId="$route.params.id"
:title="'Add New Metric '"
:mode="'Add'"
:projectMetric="viewingNewMetric"
:possibleAssignees="item.possibleAssignees"
:presetTags="projectSummary.project.presetTags"
:meetingId="$route.params.meetingId"
#submit="addProjectMetricReasonSubmit()">
</addProjectMetricModal>
Here is the code on the Parent component which should be allowing the "add-metric" modal to be seen.
props: {
title: {
type: String,
required: true,
},
addNewText: {
type: String,
},
items: {
type: Array,
required: true,
},
length: {
type: Number,
default: 6,
},
projectSummary: {
type: Object,
},
projectTags: {
type: Array,
},
status: {
type: String,
},
},
data() {
return {
currentMetric: null,
newProjectMetric: null,
add : false,
};
},
openAddModal(newMetric) {
this.newProjectMetric = newMetric;
console.log(this.newProjectMetric)
console.log("hit")
//this.closeProjectMetricSubmit();
this.$nextTick(() => {
this.add = true;
// let modal = document.getElementById(this.metricReason)
// modal.show();
// let ref = `add-project-metric-reason-modal`;
// this.$refs[ref].show();
});
},
Is there something I am missing or not understanding fully with modals? For quick example here are the HTML for the "Close-metric" and "Add-metric" modal.
<template>
<b-modal
:title="title"
v-model="showCloseModal"
:ok-disabled="editDisabled"
#ok.prevent="submit"
size="lg"
#cancel.prevent="cancel"
>
<b-form v-if="form" #submit.prevent>
<b-form-group
v-if="isPresent('name')"
:label="getTitle('name', 'Title')"
label-for="project-metric-name"
invalid-feedback="Please fill out this field"
>
<b-form-input
id="project-metric-name"
v-model.trim="form.name"
required
:disabled="editDisabled"
:autofocus="!showCloseFields"
maxlength="100"
:state="
errors.length == 0
? null
: errors.length == 0
? null
: form.name.length > 0
"
></b-form-input>
</b-form-group>
<!-- This is for only the Closed Section -->
<b-row v-if="!this.meetingId == '' && this.mode=='Close'">
<b-col cols="4" v-if="isPresent('resolutionReason')">
<b-form-group
:label="getTitle('resolutionReason', 'Resolution Reason')"
label-for="project-metric-resolution-reason"
>
<multiselect
v-model="projectMetricResolutions"
:options="otherMetricResolutions"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:preserve-search="true"
placeholder="Pick Resolution Reason"
label="name"
track-by="name"
:preselect-first="false"
class="normalFontSize"
:show-labels="false"
>
<template slot="selection" slot-scope="{ values, isOpen }"
><span
class="multiselect__single"
v-if="values.length && !isOpen"
>{{ values.length }} options selected</span
></template
>
</multiselect>
</b-form-group>
</b-col>
<b-col v-if="isPresent('resolutionLevel')">
<b-form-group
:label="getTitle('resolutionLevel', 'Resolution Level')"
label-for="project-metric-resolution-level"
>
<b-form-select
id="project-metric-resolution-level"
:disabled="editDisabled"
v-model="form.resolutionLevel"
>
<option value=""></option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</b-form-select>
</b-form-group>
</b-col>
</b-row>
<!-- This is where the Closed Section Ends -->
</b-form>
<MessageBlock :messages="errors"></MessageBlock>
<template v-slot:modal-ok>
<div>
<b-spinner v-if="sending" small label="Spinning" />
<div v-else-if="mode === 'Close'">Close</div>
</div>
</template>
</b-modal>
</template>
export default {
name: "CloseProjectMetricModal",
mixins: [pmMixin],
components: { Multiselect },
props: {
title: { type: String, default: "Edit Action Item" },
mode: { type: String, default: "Edit" },
projectMetric: { type: Object },
projectId: String,
possibleAssignees: { type: Array, default: () => [] },
addTypes: {
type: Array,
default: () => [
"Risk",
"Issue",
"Efficiency",
"Innovation",
"Partnership",
"Sustainability",
],
},
dataTemplate: Object,
presetTags: { type: Array, default: () => [] },
meetingId: { type: String, default: "" },
},
data() {
return {
errors: [],
showCloseModal: false,
sending: false,
returnToView: false,
form: null,
description: "",
filterCriteria: "",
projectMetricResolutions: [],
otherMetricResolutions: [
{ name: "Innovation", type: "Innovation" },
{ name: "Efficiency", type: "Efficiency" },
{ name: "Partnership", type: "Partnership" },
{ name: "Sustainability", type: "Sustainability" },
],
allResolutionReason: "",
todaysNewDate: new Date().toISOString().substr(0, 10),
showAddModal: false,
};
},
}
AddMetricModal
<template>
<b-modal
:title="title"
v-model="showModal"
:ok-disabled="editDisabled"
#ok.prevent="submit"
size="lg"
#cancel.prevent="cancel"
>
<b-form v-if="form" #submit.prevent>
<b-form-group
v-if="isPresent('name')"
:label="getTitle('name', 'Title')"
label-for="project-metric-name"
invalid-feedback="Please fill out this field"
>
<b-form-input
id="project-metric-name"
v-model.trim="form.name"
required
:disabled="editDisabled"
:autofocus="!showCloseFields"
maxlength="100"
:state="
errors.length == 0
? null
: errors.length == 0
? null
: form.name.length > 0
"
></b-form-input>
</b-form-group>
<b-form-group
v-if="isPresent('description')"
:label="getTitle('description')"
label-for="project-metric-description"
>
<b-form-textarea
id="project-metric-description"
v-model.trim="form.description"
:disabled="editDisabled"
maxlength="1000"
rows="3"
></b-form-textarea>
</b-form-group>
<b-row>
<b-col v-if="isPresent('type')">
<b-form-group
:label="getTitle('type')"
label-for="project-metric-type"
>
<div class="select-wrapper">
<b-form-select
id="project-metric-type"
:disabled="editDisabled"
v-model="form.type"
:options="getTypeOptions"
></b-form-select>
</div>
</b-form-group>
</b-col>
<b-col>
<b-button
v-for="(t, i) in unselectedTags"
:key="i"
class="mr-1 button-tag"
#click="addTag(t)"
>
{{ t }}
</b-button>
<b-form-group
v-if="isPresent('tags')"
:label="getTitle('tags')"
label-for="project-metric-tags"
>
<b-form-tags
id="project-metric-tags"
v-model.trim="form.tags"
separator=","
add-button-variant="secondary"
:disabled="editDisabled"
></b-form-tags>
</b-form-group>
</b-col>
</b-row>
</b-form>
<MessageBlock :messages="errors"></MessageBlock>
<template v-slot:modal-ok>
<div>
<b-spinner v-if="sending" small label="Spinning" />
<div v-else-if="mode === 'Add'">Add</div>
<div v-else-if="mode === 'Edit'">Save</div>
<div v-else-if="mode === 'Close'">Close</div>
</div>
</template>
</b-modal>
</template>
import axios from "axios";
import pmMixin from "#/mixins/project-metric-mixin.js";
import Multiselect from "vue-multiselect";
export default {
name: "EditProjectMetricModal",
mixins: [pmMixin],
components: { Multiselect },
props: {
title: { type: String, default: "Edit Action Item" },
mode: { type: String, default: "Edit" },
projectMetric: { type: Object },
projectId: String,
possibleAssignees: { type: Array, default: () => [] },
addTypes: {
type: Array,
default: () => [
"Risk",
"Issue",
"Efficiency",
"Innovation",
"Partnership",
"Sustainability",
],
},
dataTemplate: Object,
presetTags: { type: Array, default: () => [] },
meetingId: { type: String, default: "" },
},
data() {
return {
errors: [],
showModal: false,
sending: false,
returnToView: false,
form: null,
description: "",
filterCriteria: "",
uselessArray: [],
projectMetricResolutions: [],
otherMetricResolutions: [
{ name: "Innovation", type: "Innovation" },
{ name: "Efficiency", type: "Efficiency" },
{ name: "Partnership", type: "Partnership" },
{ name: "Sustainability", type: "Sustainability" },
],
allResolutionReason: "",
todaysNewDate: new Date().toISOString().substr(0, 10)
};
},
Any help or direction would be greatly appreciated !

Vue 2 : how to select just the search results inside v-data-table

I tried to use select all (as a checkbox) to select all result that what I search for, but it's still selected all data in table and I use employee.map for loop my selection all data in table. Can somebody help me?
here is my code :
<template>
<div>
<v-container>
<v-row>
<v-col cols="12" md="6" sm="8">
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-col>
</v-row>
</v-container>
<v-row>
<v-col cols="12" sm="12" md="12">
<v-data-table
:headers="headers"
:items="employee"
:single-select="singleSelect"
item-key="empname"
:search="search"
:sort-by="['check', 'id']"
:sort-desc="true"
class="elevation-1"
>
<template v-slot:item.check="{ item }">
<v-simple-checkbox v-model="item.check"></v-simple-checkbox>
</template>
</v-data-table>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="12" md="12">
<v-checkbox
label="Select All"
style="direction: rtl"
#click="allSelected()"
></v-checkbox>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
data: () => ({
singleSelect: false,
selected: [],
search: '',
headers: [
{
text: 'ID',
align: 'start',
value: 'id',
},
{ text: 'Employee Name', value: 'empname', sortable: false },
{ text: 'Job', value: 'job', sortable: false },
{ text: 'Check', value: 'check', sortable: false, align: 'center' },
],
employee: [],
}),
watch: {
dialog(val) {
val || this.close()
},
dialogDelete(val) {
val || this.closeDelete()
},
},
created() {
this.initialize()
},
methods: {
allSelected() {
this.employee.map((emp) => {
emp.check = !emp.check
console.log(emp.check)
})
},
initialize() {
this.employee = [
{
id: '1',
empname: 'Joel',
job:'Doctor',
check: false,
},
{
id: '2',
empname: 'Lisa',
job:'Nurse',
check: false,
},
{
id: '3',
empname: 'Vera',
job:'Doctor',
check: false,
},
{
id: '4',
empname: 'Leo',
job:'Nurse',
check: false,
},
]
},
},
}
</script>
if there is anything that I did wrong more than what I expected. I apologize. and Thanks for Helping me.
I'm not sure about what you want to do, if you want that checkbox to be use to select the employees, or if that checkbox represent a data about the employee (e.g: isVaccinated)
If you want to select the employees: You can use the API of v-data-table:
Add a v-model and a show-select to v-data-table:
<v-data-table
:headers="headers"
v-model="selected"
:items="employee"
:single-select="singleSelect"
show-select
item-key="empname"
:search="search"
:sort-by="['check', 'id']"
:sort-desc="true"
class="elevation-1"
>
Then you can delete the field "check" of the employees, the custom checkbox, the methods related to check and uncheck because you don't need it, everything is handle by v-data-table:
<template>
<div>
<v-container>
<v-row>
<v-col cols="12" md="6" sm="8">
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-col>
</v-row>
</v-container>
<v-row>
<v-col cols="12" sm="12" md="12">
<v-data-table
:headers="headers"
v-model="selected"
:items="employee"
:single-select="singleSelect"
show-select
item-key="empname"
:search="search"
:sort-by="['check', 'id']"
:sort-desc="true"
class="elevation-1"
>
</v-data-table>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
data: () => ({
singleSelect: false,
selected: [],
search: "",
headers: [
{
text: "ID",
align: "start",
value: "id",
},
{ text: "Employee Name", value: "empname", sortable: false },
{ text: "Job", value: "job", sortable: false },
],
employee: [],
}),
watch: {
dialog(val) {
val || this.close();
},
dialogDelete(val) {
val || this.closeDelete();
},
},
created() {
this.initialize();
},
methods: {
initialize() {
this.employee = [
{
id: "1",
empname: "Joel",
job: "Doctor",
},
{
id: "2",
empname: "Lisa",
job: "Nurse",
},
{
id: "3",
empname: "Vera",
job: "Doctor",
},
{
id: "4",
empname: "Leo",
job: "Nurse",
},
];
},
},
};
</script>
Tell me if it was the solution you expected

How to filter a bootstrap vue table with an input field

The title says it all. I want to filter my bootstrap table with an input box.
Here is my .html part of the component:
<b-table
:items="Table"
:fields="fields"
striped
small
>
</b-table>
Here is the .vue file
<template src="./jointable.component.html"> </template>
<style scoped src="./jointable.component.css"> </style>
<script>
import axios from 'axios'
export default {
name: 'jointable',
data(){
return {
Table: [],
fields: [
{key: 'client_id', label: "School Code", sortable: true},
{key: 'client_name', sortable: true},
{key: 'uuid', label: "ID", sortable: true},
{key: 'step', label: "Job Running", sortable: true},
{key: 'serverid', sortable: true},
{key: 'create_timestamp', label: "Job Start", sortable: true},
{key: 'time_elapsed', sortable: true},
{key: 'wh_db_host', sortable: true}
]
}
},
methods : {
loadData: function(){
axios.get("http://192.168.56.101:5000/jointable")
.then((res) => {
this.Table = res.data
})
.catch((err) => {
console.log(err)
})
}
},
mounted() {
this.loadData();
setInterval(function(){
this.loadData()
}.bind(this), 10000)
},
computed() {
}
}
</script>
So right now you can see that my script reloads the table every 10 seconds with updated data. This is fine. I want to also now have my table searchable/filterable. I know I have to use the computed thing but how do I use it.
Thank you to whoever helps me!
There's various ways to filter, but the most simple version is simply passing in a string to the filter prop on b-table.
This will search all columns for the string you pass in, so if you bind a data property to a input's v-model and the same data property to the filter property on your table, anything you type in the input will filter the table.
You can read more about how filtering works and some of the other methods in the documentation.
new Vue({
el: '#app',
data() {
return {
filter: '',
items: [
{ id: 1, first_name: "Mikkel", last_name: "Hansen", age: 54 },
{ id: 2, first_name: "Kasper", last_name: "Hvidt", age: 42 },
{ id: 3, first_name: "Lasse", last_name: "Boesen", age: 39 },
{ id: 4, first_name: "Kasper", last_name: "Hansen", age: 62 },
{ id: 5, first_name: "Mads", last_name: "Mikkelsen", age: 31 },
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.6.1/dist/bootstrap-vue.min.js"></script>
<link href="https://unpkg.com/bootstrap-vue#2.6.1/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<div id="app" class="p-5">
<b-input v-model="filter" placeholder="Filter table.."></b-input>
<hr />
<b-table :items="items" :fields="fields" :filter="filter">
</b-table>
</div>
Detailed Code for filtering bootstrap-vue please refer documentation Thanks
new Vue({
el: '#app',
data() {
return {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
},
{
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
},
{
isActive: false,
age: 9,
name: {
first: 'Mini',
last: 'Navarro'
},
_rowVariant: 'success'
},
{
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
},
{
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
},
{
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
},
{
isActive: true,
age: 40,
name: {
first: 'Thor',
last: 'Macdonald'
}
},
{
isActive: true,
age: 87,
name: {
first: 'Larsen',
last: 'Shaw'
},
_cellVariants: {
age: 'danger',
isActive: 'warning'
}
},
{
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
},
{
isActive: false,
age: 22,
name: {
first: 'Genevieve',
last: 'Wilson'
}
},
{
isActive: true,
age: 38,
name: {
first: 'John',
last: 'Carney'
}
},
{
isActive: false,
age: 29,
name: {
first: 'Dick',
last: 'Dunlap'
}
}
],
fields: [{
key: 'name',
label: 'Person full name',
sortable: true,
sortDirection: 'desc'
},
{
key: 'age',
label: 'Person age',
sortable: true,
class: 'text-center'
},
{
key: 'isActive',
label: 'Is Active',
formatter: (value, key, item) => {
return value ? 'Yes' : 'No'
},
sortable: true,
sortByFormatted: true,
filterByFormatted: true
},
{
key: 'actions',
label: 'Actions'
}
],
totalRows: 1,
currentPage: 1,
perPage: 5,
pageOptions: [5, 10, 15, {
value: 100,
text: "Show a lot"
}],
sortBy: '',
sortDesc: false,
sortDirection: 'asc',
filter: null,
filterOn: [],
infoModal: {
id: 'info-modal',
title: '',
content: ''
}
}
},
computed: {
sortOptions() {
// Create an options list from our fields
return this.fields
.filter(f => f.sortable)
.map(f => {
return {
text: f.label,
value: f.key
}
})
}
},
mounted() {
// Set the initial number of items
this.totalRows = this.items.length
},
methods: {
info(item, index, button) {
this.infoModal.title = `Row index: ${index}`
this.infoModal.content = JSON.stringify(item, null, 2)
this.$root.$emit('bv::show::modal', this.infoModal.id, button)
},
resetInfoModal() {
this.infoModal.title = ''
this.infoModal.content = ''
},
onFiltered(filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.6.1/dist/bootstrap-vue.min.js"></script>
<link href="https://unpkg.com/bootstrap-vue#2.6.1/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<div id="app" class="p-5">
<template>
<b-container fluid>
<!-- User Interface controls -->
<b-row>
<b-col lg="6" class="my-1">
<b-form-group
label="Sort"
label-for="sort-by-select"
label-cols-sm="3"
label-align-sm="right"
label-size="sm"
class="mb-0"
v-slot="{ ariaDescribedby }"
>
<b-input-group size="sm">
<b-form-select
id="sort-by-select"
v-model="sortBy"
:options="sortOptions"
:aria-describedby="ariaDescribedby"
class="w-75"
>
<template #first>
<option value="">-- none --</option>
</template>
</b-form-select>
<b-form-select v-model="sortDesc" :disabled="!sortBy" :aria-describedby="ariaDescribedby" size="sm" class="w-25">
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col lg="6" class="my-1">
<b-form-group label="Initial sort" label-for="initial-sort-select" label-cols-sm="3" label-align-sm="right" label-size="sm" class="mb-0">
<b-form-select id="initial-sort-select" v-model="sortDirection" :options="['asc', 'desc', 'last']" size="sm"></b-form-select>
</b-form-group>
</b-col>
<b-col lg="6" class="my-1">
<b-form-group label="Filter" label-for="filter-input" label-cols-sm="3" label-align-sm="right" label-size="sm" class="mb-0">
<b-input-group size="sm">
<b-form-input id="filter-input" v-model="filter" type="search" placeholder="Type to Search"></b-form-input>
<b-input-group-append>
<b-button :disabled="!filter" #click="filter = ''">Clear</b-button>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
<b-col lg="6" class="my-1">
<b-form-group v-model="sortDirection" label="Filter On" description="Leave all unchecked to filter on all data" label-cols-sm="3" label-align-sm="right" label-size="sm" class="mb-0" v-slot="{ ariaDescribedby }">
<b-form-checkbox-group v-model="filterOn" :aria-describedby="ariaDescribedby" class="mt-1">
<b-form-checkbox value="name">Name</b-form-checkbox>
<b-form-checkbox value="age">Age</b-form-checkbox>
<b-form-checkbox value="isActive">Active</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
</b-col>
<b-col sm="5" md="6" class="my-1">
<b-form-group label="Per page" label-for="per-page-select" label-cols-sm="6" label-cols-md="4" label-cols-lg="3" label-align-sm="right" label-size="sm" class="mb-0">
<b-form-select id="per-page-select" v-model="perPage" :options="pageOptions" size="sm"></b-form-select>
</b-form-group>
</b-col>
<b-col sm="7" md="6" class="my-1">
<b-pagination v-model="currentPage" :total-rows="totalRows" :per-page="perPage" align="fill" size="sm" class="my-0"></b-pagination>
</b-col>
</b-row>
<!-- Main table element -->
<b-table :items="items" :fields="fields" :current-page="currentPage" :per-page="perPage" :filter="filter" :filter-included-fields="filterOn" :sort-by.sync="sortBy" :sort-desc.sync="sortDesc" :sort-direction="sortDirection" stacked="md" show-empty small
#filtered="onFiltered">
<template #cell(name)="row">
{{ row.value.first }} {{ row.value.last }}
</template>
<template #cell(actions)="row">
<b-button size="sm" #click="info(row.item, row.index, $event.target)" class="mr-1">
Info modal
</b-button>
<b-button size="sm" #click="row.toggleDetails">
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
</b-button>
</template>
<template #row-details="row">
<b-card>
<ul>
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value }}</li>
</ul>
</b-card>
</template>
</b-table>
<!-- Info modal -->
<b-modal :id="infoModal.id" :title="infoModal.title" ok-only #hide="resetInfoModal">
<pre>{{ infoModal.content }}</pre>
</b-modal>
</b-container>
</template>
</div>
Is this what you are looking for?
new Vue({
el: "#app",
data: {
Items: [
{Name: 'Lorem ipsum'},
{Name: 'consectetur'},
{Name: 'adipisicing.'}
],
search: ''
},
computed: {
filteredItems() {
return this.Items.filter(item => item.Name.toLowerCase().includes(this.search.toLowerCase()))
}
}
})
.item {
border: solid black 2px;
padding: 10px;
margin: 10px;
}
.search {
border: solid black 2px;
padding: 10px;
margin: 10px;
}
p {
padding: 10px;
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<p>Found {{filteredItems.length}} items</p>
<input class="search" type="text" placeholder="Search" v-model="search">
<div class="item" v-for="item in filteredItems">{{item.Name}}</div>
</div>

How to use input with radio prepend to select one of some such inputs

From documentation of bootsrap-vue (based on bootstrap 4):
<b-input-group>
<b-input-group-prepend is-text>
<input type="radio" aria-label="Radio for following text input">
</b-input-group-prepend>
<b-form-input aria-label="Text input with radio button"></b-form-input>
</b-input-group>
And get this input:
I want to do a component to choosed one of 2 or more values based on this input.
For example,
selectedValue: null,
compareOptions: [{text: "Value1", value: option1}, {text: "Value2", value: option2}]
But documentation says it:
Note: you must use native radio and checkbox inputs, as
and include additional markup not required in input
groups.
How I should use code from docs to set a v-model and options to make it work as intended.
If it's interesting somebody the solution is:
Child Component
<template>
<b-container fluid>
<b-form-group
:label="formLabel"
class="mb-1"
label-class="text-center">
<b-row>
<b-col class="pl-2 pr-1">
<b-input-group
size="sm"
сlass="mb-1">
<b-input-group-prepend is-text>
<input
v-model="selectedValue"
:value="oldValue"
type="radio"
#change="sendSelected">
</b-input-group-prepend>
<b-form-input
:value="oldValue"
#input="changeValue('old', $event)" />
</b-input-group>
</b-col>
<b-col class="pl-1 pr-2">
<b-input-group
size="sm"
class="mb-1">
<b-input-group-prepend is-text>
<input
v-model="selectedValue"
:value="newValue"
type="radio"
#change="sendSelected">
</b-input-group-prepend>
<b-form-input
:value="newValue"
#input="changeValue('new', $event)" />
</b-input-group>
</b-col>
</b-row>
</b-form-group>
</b-container>
</template>
<script>
export default {
props: {
value: {
type: Object,
required: true
}
},
data () {
return {
selectedValue: null
}
},
computed: {
newValue () {
return this.value.new
},
oldValue () {
return this.value.old
},
formLabel () {
return this.value.label
}
},
created () {
},
methods: {
sendSelected () {
this.$emit('change', this.selectedValue)
},
changeValue (path, event) {
this.selectedValue = null
this.emitMutationEvent(path, event) //custom event
}
}
}
</script>
Parent Component
<template>
<b-container fluid>
<compare-component
v-for="documentField in documentFields"
:key="documentField.id"
:value="documentField"
#change="setSelectedValue(documentField, ...arguments)"
#mutate="editOriginValues(documentField, ...arguments)" />
</b-container>
</template>
<script>
import CompareComponent from './CompareComponent'
export default {
components: {
CompareComponent
},
mixins: [PropMutationEventMixin],
data () {
return {
reviewDocument: null,
newSessionDocument: null,
oldSessionDocument: null,
documentFields: [
{
id: 1,
old: 'old value 1',
new: 'new value 1',
label: 'value1',
selected: ''
},
{
id: 2,
old: 'old value 2',
new: 'new value 2',
label: 'value 2',
selected: ''
},
{
id: 3,
old: 'old value 3',
new: 'new value 3',
label: 'value 3',
selected: ''
}
]
}
},
methods: {
setSelectedValue (documentField, event) {
documentField.selected = event
},
editOriginValues (documentField, path, newValue) {
documentField[path] = newValue
}
}
}
</script>

Draggable table with bootstrap vue

I have been looking for a way to drag and drop rows on a Bootstrap Vue table.
I was able to find a working version here: Codepen
I have tried to implement this code to my own table:
Template:
<b-table v-sortable="sortableOptions" #click="(row) => $toast.open(`Clicked ${row.item.name}`)" :per-page="perPage" :current-page="currentPage" striped hover :items="blis" :fields="fields" :filter="filter" :sort-by.sync="sortBy" :sort-desc.sync="sortDesc" :sort-direction="sortDirection" #filtered="onFiltered">
<template slot="move" slot-scope="row">
<i class="fa fa-arrows-alt"></i>
</template>
<template slot="actions" slot-scope="row">
<b-btn :href="'/bli/'+row.item.id" variant="light" size="sm" #click.stop="details(cell.item,cell.index,$event.target)"><i class="fa fa-pencil"></i></b-btn>
<b-btn variant="light" size="sm" #click.stop="details(cell.item,cell.index,$event.target)"><i class="fa fa-trash"></i></b-btn>
</template>
<template slot="priority" slot-scope="row">
<input v-model="row.item.priority" #keyup.enter="row.item.focussed = false; updatePriority(row.item), $emit('update')" #blur="row.item.focussed = false" #focus="row.item.focussed = true" class="form-control" type="number" name="priority" >
</template>
</b-table>
Script:
import Buefy from 'buefy';
Vue.use(Buefy);
const createSortable = (el, options, vnode) => {
return Sortable.create(el, {
...options
});
};
const sortable = {
name: 'sortable',
bind(el, binding, vnode) {
const table = el.querySelector('table');
table._sortable = createSortable(table.querySelector('tbody'), binding.value, vnode);
}
};
export default {
name: 'ExampleComponent',
directives: { sortable },
data() {
let self = this;
return {
blis: [],
currentPage: 1,
perPage: 10,
pageOptions: [ 5, 10, 15 ],
totalRows: 0,
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
sortableOptions: {
chosenClass: 'is-selected'
},
filter: null,
modalInfo: { title: 'Title', content: 'priority' },
fields: [
{
key: 'move',
sortable: true
},
///...rest of the fields
]
}
};
Now I have been getting this error: Error in directive sortable bind hook: "TypeError: Cannot read property 'querySelector' of null"
Why is it not able to find the <tbody> ?
Edit: https://jsfiddle.net/d7jqtkon/
In line const table = el.querySelector('table'); you are trying to get the table element. The var el is the table element. That is why it return null when you use querySelector
after assigning the correct table variable the error disappears
const table = el;
table._sortable = createSortable(table.querySelector("tbody"), binding.value, vnode);
Link to working fiddle
new Vue({
el: "#app",
directives: {
sortable: {
bind(el, binding, vnode) {
let self =el
Sortable.create(el.querySelector('tbody'),{
...binding.value,
vnode:vnode,
onEnd: (e) => {
let ids = el.querySelectorAll("span[id^=paper_]")
let order = []
for (let i = 0; i < ids.length; i++) {
let item = JSON.parse(ids[i].getAttribute('values'))
//extract items checkbox onChange v-model
let itemInThisData = vnode.context.items.filter(i => i.id==item.id)
order.push({
id:item.id,
paper: item.paper,
domain:item.domain,
platform: item.platform,
country:item.country,
sort_priority: item.sort_priority,
selectpaper:itemInThisData[0].selectpaper
})
}
binding.value = []
vnode.context.items = []
binding.value = order
vnode.context.items = order
console.table(vnode.context.items)
},
});
},
}
},
mounted() {
this.totalRows = this.items?this.items.length: 0
},
methods:{
onFiltered(filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
},
log(){
console.table(this.items)
console.log(this)
},
},
data(){
return {
rankOption:'default',
totalRows: 1,
currentPage: 1,
filter: null,
filterOn:[],
sortBy:'paper',
sortDesc: false,
sortableOptions: {
chosenClass: 'is-selected'
},
perPage: this.results_per_page==='Todo' ? this.items.length : this.results_per_page?this.results_per_page:50,
pageOptions: [10, 50, 100, 500,'Todo'],
sortDirection: 'asc',
fields : [
{ key: 'paper', label: 'Soporte', sortable: true},
{ key: 'domain', label: 'Dominio', sortable: true},
{ key: 'platform', label: 'Medio', sortable: true},
{ key: 'country', label: 'País', sortable: true},
{ key: 'sort_priority', label: 'Rank', sortable: true},
{ key: 'selectpaper', label: 'Selección', sortable: true},
],
items : [
{
id:12,
paper: 'Expansion',
domain:'expansion.com',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
},
{
id:13,
paper: 'El economista',
domain:'eleconomista.es',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
},
{
id:14,
paper: 'El país',
domain:'elpais.es',
platform: 'p',
country:'España',
sort_priority: '',
selectpaper:false
}
]
}
}
})
<div id="app">
<template id="">
<b-table
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
v-sortable="items"
show-empty
small
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:filterIncludedFields="filterOn"
:sort-direction="sortDirection"
#filtered="onFiltered"
>
<template v-slot:cell(selectpaper)="row">
<span :id="'paper_'+row.item.id" :values="JSON.stringify(row.item)"></span>
<b-form-group>
<input type="checkbox" #change="log" v-model="row.item.selectpaper" />
</b-form-group>
</template>
<template v-slot:cell(sort_priority)="row" v-if="rankOption==='foreach-row'">
<b-form-group>
<b-form-input type="number" #change="log"
size="sm" placeholder="Rank" v-model="row.item.sort_priority">
</b-form-input>
</b-form-group>
</template>
</b-table>
</template>
</div>
<script src="//unpkg.com/vue#latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs#latest/Sortable.min.js"></script>

Categories

Resources