I have a Laravel + Inertiajs + Vue 3 with Vite
I user Quasar framework so in one vue file I have a Quasar table.
Now I need to translate the column labels.
<template>
<head title="Elenco utenti"></head>
<AuthenticatedLayout>
<template #header>
<div>
<q-toolbar class="q-gutter-sm">
<Link :href="route('list-utenti')" type="button" class="float-right"><q-btn flat round dense icon="people" /></Link>
<div class="text-subtitle1">{{ $t('utenti.pagetitle')}}</div>
</q-toolbar>
</div>
</template>
<div class="q-py-md q-gutter-sm">
<Link :href="route('register')" type="button" class="float-right"><q-btn icon="add_box" color="white" text-color="text-grey-7" :label="$t('utenti.btn_new')"></q-btn></Link>
</div>
<div class="q-py-xl">
<q-table
title=""
:rows="users"
:columns="columns"
row-key="id"
>
<template v-slot:body-cell-actions="props">
<q-td :props="props">
<q-btn icon="mode_edit" #click="onEdit(props.row)"></q-btn>
<q-btn icon="person_off" #click="onDelete(props.row)"></q-btn>
</q-td>
</template>
<template v-slot:body-cell-email="props">
<q-td :props="props">
<strong>{{ props.value }} </strong>
</q-td>
</template>
</q-table>
</div>
</AuthenticatedLayout>
</template>
As you can see in the template code, I configured i18n and I can correctly translate with {{ $t('utenti.pagetitle') }} when in template.
now this is the script part
<script setup>
import AuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { Inertia } from '#inertiajs/inertia';
import { Link } from '#inertiajs/inertia-vue3';
import { computed, ref} from '#vue/reactivity';
import { Quasar, useQuasar } from 'quasar';
const props = defineProps({
page_description : String,
title: String,
numero_record: Number,
users: Array
});
const columns = [
{
name: 'id',
required: true,
label: 'id',
align: 'left',
field: row => row.id,
format: val => `${val}`,
sortable: false
},
{ name: 'name', align: 'left', label: translated_name, field: 'name', sortable: true},
{ name: 'email', align: 'left', label: 'e-mail', field: 'email', sortable: false},
{ name: 'ruolo', align: 'left', label: 'ruolo', field: 'ruolo', sortable: true},
{ name: 'listino_grossista_visible', align: 'center', label: 'list. grossista', field: 'listino_grossista_visible', sortable: true},
{ name: 'enabled', align: 'center', label: 'Attivo', field: 'enabled', sortable: true},
{ name: 'actions', align: 'center', label: 'Azioni'}
]
const onEdit = (row) => {
Inertia.post(route('edit-utente'),
{ row } ,
{ onBefore: () => confirm('Vuoi veramente modificarlo?')}
)
}
const onDelete = (row) => {
alert('DELETE ' + row.email);
}
</script>
I would like to use in some way $t to get the correct translation for the grid columns labels (for example 'translated_name' but I don' understand how I can do it as it is in the script part and I can not use same as in template.
I probably understood that I need to add some computed properties but I didn't exactly figured out how. Consider I'm using composition api.
const translated_name = computed(() => {
return $t('utenti.nome')
})
I checked also this link Vue-i18n not translating inside component script tags but I didn't understood how to adapt to my case.
kind regards,
Matt
Related
I have created TableTop5 component using q-table, so that I can reuse it in VisitorSummary.vue, but I have no clue how to get props which should be written in v-slot:header and v-slot:body.
VisitorSummary.vue
...
<div class="row card-wrap row-sec">
<table-top-5 title="검색어 TOP5" titleWord="검색어 TOP5"
:data="data" :columns="columns" :rowFirst="rank" // <-- it gives me errors..
:rowSec="keyword" :rowThird="visitor" :props="props" // <-- i can feel it isn't right..
>
</table-top-5>
</div>
...
<script lang="ts">
import Vue from 'vue';
import axios from 'axios';
import VueApexCharts from 'vue-apexcharts'
import NumberCount from 'components/card/NumberCount.vue'
import NumberCountSet from 'components/card/NumberCountSet.vue';
import TitleWithTooltip from 'components/card/TitleWithTooltip.vue'
import TableTop5 from 'components/table/TableTop5'
Vue.use(VueApexCharts)
Vue.component('apexchart', VueApexCharts)
export default Vue.extend({
components: {
NumberCountSet,
NumberCount,
TitleWithTooltip,
TableTop5,
},
data() {
return {
columns: [
{
name: 'rank',
required: true,
label: '순위',
align: 'center',
field: row => row.rank,
format: val => `${val}`,
sortable: true,
style: 'width: 10%;'
},
{ name: 'keyword', align: 'left', label: '검색어', field: 'keyword', sortable: true,
style: 'width: 70%; background:RGBA(0,178,45,0.05) ' },
{ name: 'visitor', align: 'center', label: '방문자수', field: 'visitor', sortable: true, style: 'width: 20%' },
],
data: [
{
rank: 1,
keyword: 'slimplanet',
visitor: 25,
},
{
rank: 2,
keyword: 'test',
visitor: 58,
},
{
rank: 3,
keyword: 'test',
visitor: 64,
},
{
rank: 4,
keyword: 'Slimplanet',
visitor: 72,
},
{
rank: 5,
keyword: 'test',
visitor: 18,
},
],
};
},
});
</script>
TableTop5.vue
<template>
<q-card class="card-item">
<q-card-section class="q-pa-none">
<div>
<q-table
:data="data"
:columns="columns"
row-key="name"
class="no-shadow q-pb-md q-px-md"
:hide-pagination="true"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
style="font-weight: bold"
>
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="rowFirst" :props="props">
{{ rowFirst }}
</q-td>
<q-td key="rowSec" :props="props">
{{ props.row.rowSec }}
</q-td>
<q-td key="rowThird" :props="props">
<q-badge color="black" outline>
{{ props.row.rowThird }}
</q-badge>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</q-card-section>
</q-card>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'TableTop5',
props: {
title: {
type: String
},
titleWord: {
type: String
},
data: {
type: Array
},
columns: {
type: Array
},
rowFirst: {
type: String
},
rowSec: {
type: String
},
rowThird: {
type: String
},
props: {
type: Object
}
}
});
</script>
Or should i just not reuse and write q-table whenever I need it in a row?
This can be done in multiple ways. You can define it like this where the parent component takes care of rendering rows and columns. You can also define some columns that every table needs to have and have a slot for extra columns.
You could also iterate over the columns so you render them dynamically.
Either way, the advantage to this approach, is consistency and reusability, in case you have some complex logic inside the table for filtering and other stuff.
TableTop5
<template>
<q-table :data="data" :columns="columns">
<template #body="props">
<slot v-bind="props" name="columns"></slot>
</template>
</q-table>
</template>
<script>
export default {
name: 'TableTop5',
props: {
data: {
type: Array,
},
columns: {
type: Array,
},
},
};
</script>
VisitorSummary
<template>
<div>
...
<table-top5 :data="data" :columns="columns">
<template #columns="props">
<q-tr>
<q-td key="c1">
{{ props.row.c1 }}
</q-td>
<q-td key="c2">
{{ props.row.c2 }}
</q-td>
</q-tr>
</template>
</table-top5>
...
</div>
</template>
<script>
import TableTop5 from '#/TableTop5.vue';
export default {
name: 'VisitorSummary',
components: {
TableTop5,
},
data() {
return {
columns: [
{
name: 'c1',
label: 'Col 1',
field: 'c1',
align: 'left',
},
{
name: 'c1',
label: 'Col 2',
field: 'c2',
align: 'left',
},
],
data: [
{
c1: 'C1 data - row 1',
c2: 'C2 data - row 1',
},
{
c1: 'C1 data - row 2',
c2: 'C2 data - row 2',
},
],
};
},
};
</script>
I have used the table component from the ant design, and right now I'm stuck on why this cannot work,
The reference link is:
https://2x.antdv.com/components/table-cn#components-table-demo-edit-cell
Here's the source code from the ant design about editable table:
<template>
<a-table :columns="columns" :data-source="dataSource" bordered>
<template v-for="col in ['name', 'age', 'address']" #[col]="{ text, record }" :key="col">
<div>
<a-input
v-if="editableData[record.key]"
v-model:value="editableData[record.key][col]"
style="margin: -5px 0"
/>
<template v-else>
{{ text }}
</template>
</div>
</template>
<template #operation="{ record }">
<div class="editable-row-operations">
<span v-if="editableData[record.key]">
<a #click="save(record.key)">Save</a>
<a-popconfirm title="Sure to cancel?" #confirm="cancel(record.key)">
<a>Cancel</a>
</a-popconfirm>
</span>
<span v-else>
<a #click="edit(record.key)">Edit</a>
</span>
</div>
</template>
</a-table>
</template>
<script lang="ts">
import { cloneDeep } from 'lodash-es';
import { defineComponent, reactive, ref, UnwrapRef } from 'vue';
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
slots: { customRender: 'name' },
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
slots: { customRender: 'age' },
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
slots: { customRender: 'address' },
},
{
title: 'operation',
dataIndex: 'operation',
slots: { customRender: 'operation' },
},
];
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
export default defineComponent({
setup() {
const dataSource = ref(data);
const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});
const edit = (key: string) => {
editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
};
const save = (key: string) => {
Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
delete editableData[key];
};
const cancel = (key: string) => {
delete editableData[key];
};
return {
dataSource,
columns,
editingKey: '',
editableData,
edit,
save,
cancel,
};
},
});
</script>
<style scoped>
.editable-row-operations a {
margin-right: 8px;
}
</style>
However, I have the functionality that my name will be a hyperlink, so I need to put the name column and its value outside the v-for. Here's what I got:
<template>
<a-table :columns="columns" :data-source="dataSource" bordered>
<template v-for="col in ['age', 'address']" #[col]="{ text, record }" :key="col">
<div>
<a-input
v-if="editableData[record.key]"
v-model:value="editableData[record.key][col]"
style="margin: -5px 0"
/>
<template v-else>
{{ text }}
</template>
</div>
<template #name="{ text, record }">
<div>
<a-input v-if="editableData[record.key]" v-model:value="editableData[record.key][record.name]" style="margin:
-5px 0"></a-input>
<router-link v-else="v-else" to="/tables/123">{{ text }}</router-link>
</div>
</template>
</template>
<template #operation="{ record }">
<div class="editable-row-operations">
<span v-if="editableData[record.key]">
<a #click="save(record.key)">Save</a>
<a-popconfirm title="Sure to cancel?" #confirm="cancel(record.key)">
<a>Cancel</a>
</a-popconfirm>
</span>
<span v-else>
<a #click="edit(record.key)">Edit</a>
</span>
</div>
</template>
</a-table>
</template>
<script lang="ts">
import { cloneDeep } from 'lodash-es';
import { defineComponent, reactive, ref, UnwrapRef } from 'vue';
const columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
slots: { customRender: 'name' },
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
slots: { customRender: 'age' },
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
slots: { customRender: 'address' },
},
{
title: 'operation',
dataIndex: 'operation',
slots: { customRender: 'operation' },
},
];
interface DataItem {
key: string;
name: string;
age: number;
address: string;
}
const data: DataItem[] = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
export default defineComponent({
setup() {
const dataSource = ref(data);
const editableData: UnwrapRef<Record<string, DataItem>> = reactive({});
const edit = (key: string) => {
editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
};
const save = (key: string) => {
Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
delete editableData[key];
};
const cancel = (key: string) => {
delete editableData[key];
};
return {
dataSource,
columns,
editingKey: '',
editableData,
edit,
save,
cancel,
};
},
});
</script>
<style scoped>
.editable-row-operations a {
margin-right: 8px;
}
</style>
However, when I changed my name through the editing mode, it seems that the name will not change based on the edited name. Is there anything I'm wrong? Any help will be greatly appreciated!
Ok, I finally got the answer - should be using a editableData[record.key].name when we are using v-model instead of editableData[record.key][record.name]
Im trying to insert a checkbox column using quasar. The way I did, it only appears as the first element of the table, and I want it as the second element. How do I do this?
Example:
export default {
name: 'DespesasGeraisBKOOnline',
data() {
return{
columns: [
{
name: 'acoes',
label: 'Ações',
field: (row) => row.acoes,
sortable: true,
align: 'center',
},
{
name: 'pc',
align: 'center',
label: 'PC ',
sortable: false,
},
{
name: 'criada_em',
label: 'Criada em',
field: (row) => row.criada_em,
sortable: true,
align: 'center',
},
{
name: 'nome',
label: 'Nome',
field: (row) => row.nome,
sortable: true,
align: 'center',
},
}
}
<q-table
:data="data"
:columns="columns"
row-key="id"
selection="multiple"
class="default-table q-mt-xs"
>
<template v-slot:no-data="{}">
<div class="full-width row flex-center no-data-finded q-gutter-sm">
<span>
{{ loading ? 'Carregando...' : 'Nenhuma despesa encontrada.' }}
</span>
</div>
</template>
</q-table>
If you run the code, you can see that the first column is the "checkbox". However, I want this as the second column.
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="text" :props="props">{{ props.row.title }}</q-td>
<q-td key="radio" :props="props"><q-radio v-model="input" :label="props.row.label" /></q-td>
<q-td key="checkbox" :props="props"><q-checkbox v-model="input" :label="props.row.label" /></q-td>
<q-td key="button" :props="props"><q-btn label="btn" #click="method"/></q-td>
</q-tr>
</template>
This is the way i usually define table if i want to insert any form control peacefully within any column
I'm using vue-good-table Vue's component but there's a problem with pagination. Basically, the first page always shows one record missing. I mean, if I set the 'perPage' option to 10, the first page shows 9 records, while all the others show 10 records. What could it be?
My code:
app.js
require('./bootstrap');
window.Vue = require('vue');
window.axios = require('axios');
Vue.component("my-table", require("./components/MyTable.vue").default);
const app = new Vue({
el: '#app',
});
MyTable.vue
<template>
<div>
<vue-good-table
#on-selected-rows-change="selectionChanged"
#on-select-all="selectAll"
:columns="columns"
:rows="rows"
styleClass="vgt-table striped condensed"
:select-options="{
enabled: true,
selectOnCheckboxOnly: true,
selectionText: 'record selezionati',
clearSelectionText: 'Pulisci',
selectionInfoClass: '',
}"
:search-options="{
enabled: true,
placeholder: 'Cerca',
}"
:sort-options="{
enabled: true,
initialSortBy: {field: 'numero_verbale', type: 'asc'}
}"
:pagination-options="{
enabled: true,
mode: 'records',
perPage: 10,
position: 'top',
perPageDropdown: [5, 10, 20],
dropdownAllowAll: true,
nextLabel: 'Prossima',
prevLabel: 'Precedente',
rowsPerPageLabel: 'Record per pagina',
ofLabel: 'di',
allLabel: 'Tutti',
}"
>
<div slot="selected-row-actions">
<button class="mr-4">Action 1</button>
<button class="mr-4">Action 2</button>
<button >this.routeURL</button>
</div>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'elimina'">
<button #click="deleteOrdinanza(props.row.id, props.index)" class="bg-grey-200 text-sm"> ELI</button>
</span>
<span v-if="props.column.field == 'dettaglio'">
<button #click="rowId(props.row.id, props.index)" class="bg-grey-200 text-sm"> DETTAGLIO</button>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
</div>
</template>
<script>
import 'vue-good-table/dist/vue-good-table.css'
import { VueGoodTable } from 'vue-good-table';
axios.defaults.baseURL = 'http://127.0.0.1:8000/api';
export default {
name: 'my-table',
props: {
ordinanze: String,
},
data(){
return {
columns: [
{
label: 'ID',
field: 'id',
type: 'number',
// hidden: true
},
{
label: 'N° Verbale',
field: 'numero_verbale',
type: 'number',
width: '130px'
},
{
label: 'Data Verbale',
field: 'data_verbale',
dateInputFormat: 'yyyy-MM-dd',
dateOutputFormat: 'MMM Do yy',
},
{
label: 'Cognome',
field: 'cognome',
},
{
label: 'Nome',
field: 'nome',
},
{
label: 'Codice Fiscale',
field: 'codice_fiscale',
},
{
label: 'Città',
field: 'citta',
},
{
label: 'Provincia',
field: 'provincia'
},
{
label: 'Data Notifica',
field: 'data_notifica',
dateInputFormat: 'yyyy-MM-dd',
dateOutputFormat: 'MMM Do yy',
},
{
label: '',
field: 'elimina',
sortable: false,
},
{
label: '',
field: 'dettaglio',
sortable: false,
},
],
rows : JSON.parse(this.ordinanze) ,
};
},
methods: {
rowId(idParam) {
console.log(idParam);
},
deleteOrdinanza(id, index){
console.log(id);
console.log(index);
axios.delete('/ordinanze/' + id)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error)
});
this.rows.splice(index,1);
}
},
components: {
VueGoodTable,
},
};
</script>
vuegoodtable.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue comp test </title>
<script src="/js/app.js" defer></script>
</head>
<body>
{{-- table component--}}
{{-- $ordinanze is the array with all the table records--}}
<div id="app" class="-mx-4 sm:-mx-8 px-4 sm:px-8 py-3">
<my-table :ordinanze='#json($ordinanze)'></my-table>
</div>
</body>
</html>
Thanks!
I'm wondering if you are using vue-good-table v2.19.2, because I've seen your problem with that version.
If so, you will not see this problem using 2.19.1 or 2.19.3 (that is the latest version currently).
I'm having an issue with my small application. I currently have a job page, I'm able to post and get the jobs.
On the frontend I have a button to press Delete but when I do it keeps giving me this error - DELETE FROM 'jobs' WHERE 'id' = 'undefined'.
Currently, technologies being used ar MySQL, sequelize, node.js, express, and vue.js.
Console
<div>
<h2 class="mb-4 font-weight-light">Job postings</h2>
<div class="d-flex align-items-center justify-content-between">
<b-input-group class="w-30">
<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-button variant="primary" class="d-flex align-items-center" v-b-modal.addJob><i class="material-icons mr-1"></i> Add job</b-button>
</div>
<b-table responsive hover :items="jobs" :fields="fields" :filter="filter" no-sort-reset sort-by="postedOn" :sort-desc="true" class="mt-3 f6">
<template slot="job_postingURL" slot-scope="data">
<a :href="`${data.value}`" target="_blank">{{ data.value }}</a>
</template>
<template slot="Remove" scope="jobs">
<b-btn variant="danger" #click="deleteJob(jobs.ID)"> Delete </b-btn>
</template>
</b-table>
<add-job></add-job>
</div>
</template>
<script>
import AddJob from '#/components/jobs/AddJob'
import JobService from '../../services/JobService'
import axios from 'axios'
export default {
components: {
AddJob
},
data () {
return {
fields: [
{ Key: 'ID', label: 'Job ID', sortable: false},
{ key: 'job_title', label: 'Job title', sortable: true },
{ key: 'job_name', label: 'Company name', sortable: true },
{ key: 'job_location', label: 'Location', sortable: true },
{ key: 'job_postingURL', label: 'Job posting link', sortable: false },
{ key: 'job_postingOn', label: 'Posted on', sortable: true, tdClass: 'text-right' },
{ key: 'job_postingBy', label: 'Posted by', sortable: true },
{ key: 'Remove', sortable: true }
],
filter: null,
jobs: [
{
ID: '',
job_title: '',
job_name: '',
job_location: '',
job_postingURL: '',
job_postingOn: '',
job_postingBy: ''
},
],
}
},
// this method is to get the data from database
async created () {
try {
this.jobs = await JobService.getJobs();
} catch(err) {
this.error = err.message;
}
},
methods: {
deleteJob (ID) {
axios.delete(`http://localhost:5000/api/jobs/${this.ID}`)
.then((res) => {
this.ID = ''
this.job_title = ''
this.job_name = ''
this.job_location = ''
this.job_postingURL =''
this.job_postingOn = ''
this.job_postingBy = ''
console.log(res)
})
.catch((err) => {
console.log(err)
})
}
}
}
</script>
Since you have jobs like a data object property you couldn't use jobs as slot-scope value, try something like row, in this case row object contains some properties like item which contain data about the current item shown in this row, so you should do :
<template slot="Remove" slot-scope="row">
<b-btn variant="danger" #click="deleteJob(row.item.ID)"> Delete </b-btn>
</template>
and in your method :
deleteJob (ID) {
axios.delete('http://localhost:5000/api/jobs/'+ID)
.then((res) => {...