i am using vue-bootstrap4-table in my application i have a custom input though which i search and populate the data,now i need to build a feature in which their is a cross button inside the search field and on clicking on it it should reset the table to empty state here is my code
<template>
<auto-complete
#autocomplete-result-selected="setCustomer"
placeholder="Enter Customer name"
:selected="selectedCustomer"
:styles="{width: 'calc(100% - 10px)'}"
index="locations"
attribute="name"
>
<template slot-scope="{ hit }">
<span>
{{ hit.customer.company && hit.customer.company + ' - ' }}{{ hit.customer.fname }}
{{ hit.customer.lname }}
</span>
</template>
</auto-complete>
<i class="fas fa-times" #click="clearTable()" v-show="selectedCustomer"></i>
</div>
</div>
</template>
<script>
import http from "../../helpers/api.js";
import AutoComplete from "../autocomplete/Autocomplete";
import axios from "axios";
import VueBootstrap4Table from "vue-bootstrap4-table";
export default {
components: {
"auto-complete": AutoComplete,
VueBootstrap4Table
},
computed: {},
data() {
return {
value: "",
selectedCustomer: "",
selectedFirstName: "",
selectedLastName: "",
selectedFields: [
{ name: "Invoice", value: "invoices" },
{
name: "Estimate",
value: "workorder_estimates"
}
],
filters: [
{ is_checked: true, value: "invoices", name: "Invoice" },
{ is_checked: true, value: "workorder_estimates", name: "Estimate" }
],
selectedFilters: [],
estimateChecked: false,
invoiceChecked: false,
config: {
card_mode: false,
show_refresh_button: false,
show_reset_button: false,
global_search: {
placeholder: "Enter custom Search text",
visibility: false,
case_sensitive: false
},
pagination: true,
pagination_info: false,
per_page: 10,
rows_selectable: true,
server_mode: true,
preservePageOnDataChange: true,
selected_rows_info:true
},
classes: {},
rows: [],
columns: [
{
label: "TYPE",
name: "type"
},
{
label: "ID",
name: "distinction_id"
},
{
label: "CUSTOMER NAME",
name: "full_name"
},
{
label: "SERVICE DATE",
name: "service_date"
},
{
label: "ADDRESS",
name: "address1"
},
{
label: "CREATE DATE",
name: "created_at"
}
],
queryParams: {
sort: [],
filters: [],
global_search: "",
per_page: 10,
page: 1
},
selected_rows: [],
total_rows: 0
};
},
methods: {
onNameSearch() {
this.selectedFilters = ["invoices", "workorder_estimates"];
this.fetchData();
},
clearTable(){
this.rows=[];
console.log(this.config.selected_rows_info);
this.config.selected_rows_info=false;
},
onChangeQuery(queryParams) {
console.log(queryParams);
this.queryParams = queryParams;
this.fetchData();
},
onRowClick(payload) {
console.log(payload);
},
setCustomer(selectedResult) {
this.selectedCustomer = selectedResult.customer.company
? `${selectedResult.customer.company + " - "}${
selectedResult.customer.fname
} ${selectedResult.customer.lname}`
: `${selectedResult.customer.fname} ${selectedResult.customer.lname}`;
this.selectedFirstName = selectedResult.customer.fname;
this.selectedLastName = selectedResult.customer.lname;
},
changeCheck(event, index, value) {
var checked = event.target.checked;
switch (value) {
case "invoices":
if (checked) {
this.selectedFields.push({ name: "Invoice", value: "invoices" });
this.invoiceChecked = true;
var data = this.filters[index];
data.is_checked = true;
Vue.set(this.filters, data, index);
} else {
var index = this.selectedFields.findIndex(
item => item.value === value
);
this.selectedFields.splice(index, 1);
this.invoiceChecked = false;
var data = this.filters[index];
data.is_checked = false;
Vue.set(this.filters, data, index);
}
break;
case "workorder_estimates":
if (checked) {
this.selectedFields.push({
name: "Estimate",
value: "workorder_estimates"
});
var data = this.filters[index];
data.is_checked = true;
Vue.set(this.filters, data, index);
} else {
var index = this.selectedFields.findIndex(
item => item.value === value
);
this.selectedFields.splice(index, 1);
this.estimateChecked = false;
var data = this.filters[index];
data.is_checked = false;
Vue.set(this.filters, data, index);
}
break;
}
},
removeFilter(index, value) {
switch (value) {
case "workorder_estimates":
this.selectedFields.splice(index, 1);
this.estimateChecked = false;
var i = this.filters.findIndex(item => item.value === value);
var data = this.filters[i];
data.is_checked = false;
Vue.set(this.filters, data, i);
break;
case "invoices":
this.selectedFields.splice(index, 1);
this.invoiceChecked = false;
var i = this.filters.findIndex(item => item.value === value);
var data = this.filters[i];
data.is_checked = false;
Vue.set(this.filters, data, i);
break;
}
},
updateFilters() {
this.selectedFilters = [];
this.selectedFields.forEach(element => {
this.selectedFilters.push(element.value);
});
if(this.selectedFilters.length == 0){
this.selectedFilters = ['invoices', 'workorder_estimates'];
}
this.fetchData();
},
async fetchData() {
var final = [];
try {
var result = await http.post("/estimate-invoice-search", {
type: this.selectedFilters,
search: {
value: this.selectedFirstName + " " + this.selectedLastName
},
per_page: this.queryParams.per_page,
page: this.queryParams.page,
sort: this.queryParams.sort
});
this.total_rows = result.recordsFiltered;
result.data.forEach(element => {
element.full_name = element.first_name + " " + element.last_name;
final.push(element);
});
this.rows = final;
} catch (error) {
console.log(error);
}
}
},
mounted() {}
};
</script>
now the method named clearTable here i want to reset my table to the point lie we see on page refresh in the method i used this.rows=[]; this clears all the rows which is exactly what i want but the text which shows the number of rows is still their and i cant seem to remove it please view the below image for clarification
i read the documentation on link but cant seem to find a solution for hiding the text, is their any way?
It looks like you're using total_rows as the variable for the number of rows in your template here:
<span>{{total_rows}}</span> Result(s)
The only spot in code that you set this value is in fetchData() where you set:
this.total_rows = result.recordsFiltered;
You can either:
1) Make total_rows a computed property (recommended) that returns the length of rows (I believe rows is always the same length as total_rows from your code)
-or-
2) Just set this.total_rows = 0; in your clearTable() function
Related
When a user try to hover on a dot, it only displays a blue line but it does not show a tooltip as a result below
lib version:
"chart.js": "^2.9.3",
"chartjs-plugin-annotation": "^0.5.7"
Actual : enter image description here
Expected: enter image description here In chart configuration
export chart = () => {
data:{ ... },
options: {
tooltips: {
displayColors: false,
mode: 'index', intersect: true,
callbacks: {
label: function (tooltipItem, data) {
return `${data.datasets[tooltipItem.datasetIndex].label}(${Math.round(tooltipItem.xLabel * 100) / 100
},${Math.round(tooltipItem.yLabel * 100) / 100})`;
},
},
},
legend: {
display: false,
},
annotation: {
drawTime: "afterDatasetsDraw",
events: ["mouseover"],
annotations: [],
},
}
}
convertToHoverLine();
export const convertToHoverLine = (value, scaleID) => {
return {
key: "hoverLine",
type: "line",
mode: "vertical",
scaleID,
value,
borderColor: "blue",
onMouseout: null,
onMouseover: null,
}
}
handleHoverChart(); => this function will trigger when a user hover a dot on the chart
export const handleHoverChart = (myChart, x, scaleID) => {
const indexLine = myChart.options.annotation.annotations.findIndex(i => i.key === "hoverLine")
if (indexLine === -1) {
myChart.options.annotation.annotations.push(convertToHoverLine(x,scaleID))
} else {
myChart.options.annotation.annotations[indexLine] = convertToHoverLine(x,scaleID);
}
myChart.update()
}
I solved this problem by move chart update into the condition
export const handleHoverChart = (myChart, x, scaleID) => {
const indexLine = myChart.options.annotation.annotations.findIndex(i => i.key === "hoverLine")
if(indexLine === -1){
myChart.options.annotation.annotations.push(convertToHoverLine(x,scaleID))
myChart.update()
}else{
myChart.options.annotation.annotations[indexLine] = convertToHoverLine(x,scaleID);
}
}
I;m new on Vuejs and I'm currently working with composition API so I have an array like this:
const tabs = ref([
{
id: 1,
pdf: 'name1',
...
},
{
id: 2,
pdf: 'name2',
...
},
{
id: 3,
pdf: 'name3',
...
},
])
Then I have a div like this:
<div
v-for="tab in tabs"
:key="tab.name"
:href="tab.href"
class="px-12 pt-8 flex flex-col"
:class="[tab.current || 'hidden']"
#click="changeTab(tab)"
>
<div v-if="pdf != ''">
<div class="pt-4 font-bold underline">
<a :href="pdfSrc" target="_blank">See PDF</a>
</div>
</div>
</div>
And then I use computed to get current href value as:
props: {
tabs: {
type: Array as PropType<Array<any>>,
required: true,
},
},
computed: {
pdfSrc(): string {
return `/img/modal/pdf/${encodeURIComponent(this.tabs[0].pdf)}.pdf`
},
}
As you can see I always use tabs[0] so pdf value is always value name1 and I want to get depending of the selected tab
The tab method:
setup(props) {
const changeTab = (selectedTab: { id: number }) => {
props.tabs?.map((t) => {
t.id === selectedTab.id ? (t.current = true) : (t.current = false)
})
}
return {
changeTab,
}
},
How can I change static index 0 to dynamic one depending on the current tab?
I would suggest creating a new variable for tracking the selected tab.
const selectedTabId = ref(0);
Similar to tabs, this could be passed down in array and the value updated in changeTab function.
props: {
tabs: {
type: Array as PropType<Array<any>>,
required: true,
},
selectedTabId: {
type: Number
}
},
setup(props) {
const changeTab = (selectedTab: { id: number }) => {
selectedTabId = selectedTab.id
props.tabs?.map((t) => {
t.id === selectedTab.id ? (t.current = true) : (t.current = false)
})
}
return {
changeTab,
}
},
Finally in the computed use selectedTabId
computed: {
pdfSrc(): string {
return `/img/modal/pdf/${encodeURIComponent(this.tabs[this.selectedTabId].pdf)}.pdf`
},
}
I have one custom module and I need to get properties list in this module as a multi-select.
How it's possible in shopware 6?
I did try below code, but I can't get it.
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/custom-product-detail.html.twig
{% block custom_product_detail %}
<sw-page class="custom-product-detail">
<template slot="smart-bar-actions">
<sw-button
:disabled="isLoading"
:routerLink="{ name: 'custom.product.list' }">
{{ $t('custom-product.detail.actions.cancel-button') }}
</sw-button>
<sw-button-process
:isLoading="isLoading"
custom="primary"
#process-finish="saveFinish"
#click="onClickSave">
{{ $t('custom-product.detail.actions.save-button') }}
</sw-button-process>
</template>
<template slot="content">
<sw-card-view>
<sw-card
v-if="module"
:isLoading="isLoading"
class="information-card">
<sw-entity-multi-select
:label="$t('custom-product.detail.assignPropertyLabel')"
v-model="module.propertyGroupTranslation">
</sw-entity-multi-select>
</sw-card>
</sw-card-view>
</template>
</sw-page>
{% endblock %}
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/index.js
import template from './custom-product-detail.html.twig';
const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
const newLocal = 'custom_product';
Component.register('custom-product-detail', {
template,
inject: [
'repositoryFactory',
],
mixins: [
Mixin.getByName('notification')
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data() {
return {
module: null,
isLoading: false,
processSuccess: false,
repository: null
};
},
created() {
this.repository = this.repositoryFactory.create(newLocal);
this.getCustom();
},
computed: {
propertyGroupRepository() {
return this.repositoryFactory.create('property_group');
},
customProperties() {
const criteria = new Criteria();
criteria.addFilter(Criteria.equals('relations.entityName', 'property_group'));
return criteria;
}
},
methods: {
getCustom() {
this.customProperties();
const criteria = new Criteria();
criteria.addAssociation('propertyGroupTranslation');
this.repository
.get(this.$route.params.id, Shopware.Context.api, criteria)
.then((entity) => {
this.module = entity;
});
},
onClickSave() {
this.isLoading = true;
this.repository
.save(this.module, Shopware.Context.api)
.then(() => {
this.getCustom();
this.isLoading = false;
this.processSuccess = true;
}).catch((exception) => {
this.isLoading = false;
this.createNotificationError({
title: this.$t('custom-variant-product.detail.error-message'),
message: exception
});
});
},
saveFinish() {
this.processSuccess = false;
}
}
});
I got the solution.
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/custom-product-detail.html.twig
<sw-entity-multi-select
:label="$t('custom-product.detail.assignPropertyLabel')"
v-model="module.propertyGroupTranslation">
</sw-entity-multi-select>
replace to
<sw-multi-select
v-if="customProduct"
:label="$t('custom-product.detail.assign-property-group-options')"
:options="propertyOpt"
labelProperty="translated.name"
valueProperty="id">
</sw-multi-select>
custom/plugins/CustomProduct/src/Resources/app/administration/src/module/custom-product/page/custom-product-detail/index.js
import template from './custom-product-detail.html.twig';
const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('custom-product-detail', {
template,
inject: [
'repositoryFactory'
],
mixins: [
Mixin.getByName('notification')
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data() {
return {
customProductId: undefined,
customProduct: {},
isLoading: false,
processSuccess: false,
propertyOpt: [],
};
},
created() {
this.createdComponent();
},
computed: {
propertyOptionCriteria() {
const criteria = new Criteria();
criteria.addAssociation('group');
return criteria;
},
customRepository() {
return this.repositoryFactory.create('custom_product');
},
propertyOptionRepository() {
return this.repositoryFactory.create('property_group_option');
},
},
methods: {
getModule() {
this.customRepository.get(this.$route.params.id, Shopware.Context.api).then((entity) => {
this.customProduct = entity;
this.isLoading = false;
});
},
createdComponent() {
this.isLoading = true;
if (this.$route.params.id && this.customProduct.isLoading !== true) {
this.customProductId = this.$route.params.id;
this.loadPropertyOption();
}
this.isLoading = false;
},
loadPropertyOption() {
return this.propertyOptionRepository.search(this.propertyOptionCriteria, Shopware.Context.api)
.then((propertyOption) => {
this.propertyOpt = propertyOption;
});
},
}
});
I noticed a bug with my vuetify server-side data table. When a data table contains no data and then I attempt to add a new one, it will be successful but it is not displayed in the table, I even checked my database. After I hit the refresh button of the browser, it now shows.
But when a data already exist (even just one), the newly added data automatically reflects in the table. I don't know what is causing this behavior and it only happens when there is no data. I think it has something to do with an empty table being rendered.
Template
<v-data-table
:headers="headers"
:items="tableData"
:editItem="this.editItem"
:deleteItem="this.deleteItem"
:options.sync="pagination"
:server-items-length="totalData"
:loading="loading"
dense
>
Script
export default {
data() {
return {
editedIndex: -1,
search: "",
loading: true,
pagination: {},
dialog: false,
valid: false,
validationErrors: '',
tableData: [],
totalData: 0,
departments: [],
tableItem: {
name: '',
departmend_id: '',
},
snackbar: {
enable: false,
name: '',
message: '',
},
headers: [
{ text: 'ID', value: 'id' },
{ text: 'Department Name', value: 'name' },
{ text: 'From Department', value: 'department' },
{ text: 'Created At', value: 'created_at' },
{ text: 'Action', value: 'action' },
],
rules: {
name: [
v => !!v || 'This field is required',
v => (v && v.length <= 50) || 'Field must be less than 50 characters'
]
}
}
},
watch: {
params: {
handler() {
this.getDataFromApi().then(data => {
this.tableData = data.items
this.totalData = data.total
})
},
deep: true
}
},
computed: {
params(nv) {
return {
...this.pagination,
query: this.search
}
},
formTitle() {
return this.editedIndex === -1 ? 'New Section' : this.tableItem.name
},
},
mounted() {
this.getDataFromApi().then(data => {
this.tableData = data.items
this.totalData = data.total
})
},
created() {
this.getDepartments()
},
methods: {
async getDataFromApi() {
this.loading = true
return new Promise((resolve, reject) => {
const { sortBy, sortDesc, page, itemsPerPage } = this.pagination
let search = this.search.trim().toLowerCase()
axios.get('/api/sections').then(res => {
let items = _.orderBy(res.data, ['created_at'], ['desc'])
const total = items.length
if (search) {
items = items.filter(item => {
return Object.values(item).join(",").toLowerCase().includes(search)
})
}
if (sortBy.length === 1 && sortDesc.length === 1) {
items = items.sort((a, b) => {
const sortA = a[sortBy[0]]
const sortB = b[sortBy[0]]
if (sortDesc[0]) {
if (sortA < sortB) return 1
if (sortA > sortB) return -1
return 0
} else {
if (sortA < sortB) return -1
if (sortA > sortB) return 1
return 0
}
})
}
if (itemsPerPage > 0) {
items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
}
setTimeout(() => {
this.loading = false
resolve({
items,
total
})
}, 300)
})
})
},
async initialize() {
this.loading = true
let res = await axios.get('/api/sections')
this.tableData = _.orderBy(res.data, ['created_at'], ['desc'])
setTimeout(() => {
this.loading = false
}, 300)
},
async getDepartments() {
let res = await axios.get('/api/departments')
this.departments = _.orderBy(res.data, ['name'], ['asc'])
},
reset() {
this.$refs.form.reset()
},
close() {
this.dialog = false
this.$refs.form.reset()
setTimeout(() => {
this.editedIndex = -1
}, 300)
},
editItem(item) {
this.editedIndex = this.tableData.indexOf(item)
this.tableItem = Object.assign({}, item)
this.dialog = true
},
updateSnackbar(e) {
this.snackbar.enable = e
},
async deleteItem(item) {
let index = this.tableData.indexOf(item)
if(confirm('Are you sure you want to delete this item?') && this.tableData.splice(index, 1)) {
let res = await axios.delete('/api/sections/' + item.id)
this.snackbar.name = ''
this.snackbar.message = res.data.message
this.snackbar.enable = true
}
},
async save() {
if (this.editedIndex > -1) {
try {
Object.assign(this.tableData[this.editedIndex], this.tableItem)
let res = await axios.put('/api/sections/' + this.tableItem.id, this.tableItem)
this.snackbar.name = res.data.name
this.snackbar.message = res.data.message
} catch(error) {
if (error.response.status == 422){
this.validationErrors = error.response.data.errors
}
}
} else {
try {
let res = await axios.post('/api/sections', this.tableItem)
this.snackbar.name = res.data.name
this.snackbar.message = res.data.message
} catch(error) {
if (error.response.status == 422){
this.validationErrors = error.response.data.errors
}
}
}
await this.initialize()
this.snackbar.enable = true
this.close()
this.reset()
},
}
}
So after countless of trials, I have finally found the solution.
try {
let res = await axios.post('/api/departments', this.tableItem)
this.getDataFromApi().then(data => {
this.tableData = data.items
this.totalData = data.total
})
}
I'm trying to use a Vue table 2 filter to filter data by date, unfortunately it is not wroking and I am not able to find the reason. Has anyone tried such multiple filters with Vue table 2?
I went through the documentation but cannot find a solution.
https://matanya.gitbook.io/vue-tables-2/custom-filters
Html code to filter the data by date
<div class="col-md-4">
<div class="form-group">
<label for="sel1">Start Date:</label>
<input type="text" class="form-control" #keyup="applyFilterSearchText(searchText)" v-model="searchText" placeholder="End date" />
</div>
</div>
import { Event } from "vue-tables-2";
import axios from "axios";
export default {
title: "HelloWorld",
props: {
msg: String
},
data() {
return {
letters: ["Filled", "Unfilled", "Expired"],
selectedLetter: "",
searchText: "",
columns: ["refNumber", "vacancyTitle", "sector", "startDate", "endDate", "vacancyStatus"],
//data: getdetails(),
options: {
headings: {
refNumber: "Ref",
vacancyTitle: "Title",
sector: "Sector",
startDate: "Start",
endDate: "End",
vacancyStatus: "Status"
},
customFilters: [
{
name: "alphabet",
callback: function(row, query) {
return row.vacancyStatus == query;
}
},
{
name: "datefilter",
callback: function(row, query) {
return row.startDate == query;
}
}
],
// filterAlgorithm: {
// textsearch(row, query) {
// return (row.title).includes(query);
// }
// },
sortable: ["startDate", "vacancyTitle","endDate"],
sortIcon: {
base: "fa",
is: "fa-sort",
up: "fa-sort-asc",
down: "fa-sort-desc"
},
texts: {
filter: "Search by text:"
}
},
tableData:[],
};
},
methods: {
applyFilter(event) {
this.selectedLetter = event.target.value;
Event.$emit("vue-tables.filter::alphabet", this.selectedLetter);
},
applyFilterSearchText() {
console.log(this.searchText,"heiiiiiiiiiiii");
Event.$emit("vue-tables.filter::datefilter", this.searchText);
},
getdetails(){
axios.get("https://localhost:44399/api/Vacancy")
.then((res) => {
console.log(res.data,"ressssssss");
this.tableData = res.data;
})
.catch(function(error) {
console.log("Error: ", error);
});
}
},
mounted() {
this.getdetails();
}
};
A potential solution to that is to sort the data before using it in your data-table. Start by creating a computed of your data but with all your potential filters in it and create data variables with "parameters" (sort direction, sort column...)
export default {
title: "HelloWorld",
props: {
msg: String
},
data () {
return {
yourData: [],
sortBy: 'name',
sortDir: 'asc',
filterSearch: ''
}
},
computed: {
filteredData () {
if (filterSearch != '') {
let _this = this;
return this.sortedData.filter(item => {
return item.name.toLowerCase().includes(_this.filterSearch.toLowerCase());
})
} else {
return this.sortedData;
}
},
sortedData() {
return this.yourData.sort((a, b) => {
let modifier = 1;
if (this.sortBy == "order") {
if (this.sortDir === 'asc') {
return a[this.sortBy] - b[this.sortBy]
} else if (this.sortDir === 'desc') {
return b[this.sortBy] - a[this.sortBy]
}
} else {
if (this.sortDir === 'desc') modifier = -1;
if (a[this.sortBy] < b[this.sortBy]) return -1 * modifier;
if (a[this.sortBy] > b[this.sortBy]) return modifier;
return 0;
}
});
}
}
}
With that you just have to replace the props value you use to pass data to your VueTables