I make my first project in Vue.
I have small problem. I need to get a value from Datatable (marked in code: console.log (self.selectedContent); // here is my result @@) - to my main view and display it. How can I do this?
My Main.vue:
<template>
<CCard>
<CCardHeader>
<CIcon name="cil-input-power"/>
Dodaj zlecenie
</CCardHeader>
<CCardBody>
<CRow>
<CCol sm="12">
<CForm>
<div id="myTabContent">
<div : id="tab1">
<h4 >Dane przewoźnika </h4>
<CButton
@click="warningModal = true"
color="primary"
>
Uzupełnij
</CButton>
<CInput
v-model.trim="$v.form.carrier_id.$model"
:isValid="checkIfValid('carrier_id')"
type="hidden"
/>
<CRow>
<CCol md="6">
<CInput
maxlength="255"
label="Nazwa przewoźnika*"
v-model.trim="$v.form.carrier_name.$model"
:isValid="checkIfValid('carrier_name')"
placeholder="Wpisz nazwę przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
<CCol md="6">
<CInput
maxlength="255"
label="NIP przewoźnika*"
v-model.trim="$v.form.carrier_nip.$model"
:isValid="checkIfValid('carrier_nip')"
placeholder="Wpisz NIP przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
</CRow>
<CRow>
<CCol md="6">
<CInput
maxlength="255"
label="Regon przewoźnika*"
v-model.trim="$v.form.carrier_regon.$model"
:isValid="checkIfValid('carrier_regon')"
placeholder="Wpisz regon przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
<CCol md="6">
<CInput
maxlength="255"
label="Email przewoźnika*"
v-model.trim="$v.form.carrier_email.$model"
:isValid="checkIfValid('carrier_email')"
placeholder="Wpisz email przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
</CRow>
<CRow>
<CCol md="6">
<CInput
maxlength="255"
label="Telefon przewoźnika*"
v-model.trim="$v.form.carrier_phone.$model"
:isValid="checkIfValid('carrier_phone')"
placeholder="Wpisz telefon przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
<CCol md="6">
<CInput
maxlength="255"
label="Kod pocztowy przewodnika*"
v-model.trim="$v.form.carrier_postal_code.$model"
:isValid="checkIfValid('carrier_postal_code')"
placeholder="Wpisz kod pocztowy przewodnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
</CRow>
<CRow>
<CCol md="6">
<CInput
maxlength="255"
label="Adres przewoźnika*"
v-model.trim="$v.form.carrier_street.$model"
:isValid="checkIfValid('carrier_street')"
placeholder="Wpisz adres przewoźnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
<CCol md="6">
<CInput
maxlength="255"
label="Miasto przewodnika*"
v-model.trim="$v.form.carrier_city.$model"
:isValid="checkIfValid('carrier_city')"
placeholder="Wpisz miasto przewodnika"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
</CRow>
<CRow>
<CCol md="12">
<CInput
maxlength="255"
label="Email do powiadomień*"
v-model.trim="$v.form.carrier_email_to_notification.$model"
:isValid="checkIfValid('carrier_email_to_notification')"
placeholder="Wpisz email do powiadomień"
invalidFeedback="To pole jest wymagane i musi zawierać minimum 3 znaki i maksimum 255 znaków"
/>
</CCol>
</CRow>
<h4 >Kierowca </h4>
<CRow>
<CCol md="12">
<CSelect id="driver_id"
label="Wybierz kierowcę"
v-model.trim="$v.form.driver_id.$model"
:options="drivers"
>
</CSelect>
</CCol>
</CRow>
<h4 >Rodzaj przewożonego towaru </h4>
<template v-for="(option) in productDircionary">
<div :key="option.name">
<CCol sm="12">
<input type="checkbox" name="selectedProducts[]" :value="option.value" v-model="selectedProducts"
:id="option.value" :checked="selectedProducts.includes(option.value)"/> {{ option.label }}
</CCol>
</div>
</template>
<div>wybrano: {{ selectedProducts }}</div>
<h4 >Wymagane dokumenty </h4>
<template v-for="(option) in documentDircionary">
<div :key="option.name">
<CCol sm="12">
<input type="checkbox" name="selectedDocuments[]" :value="option.value"
v-model="selectedDocuments" :id="option.value"
:checked="selectedDocuments.includes(option.value)"/> {{ option.label }}
</CCol>
</div>
</template>
<div>wybrano: {{ selectedDocuments }}</div>
<h4 >Wymagane pliki dla dostawcy </h4>
<template v-for="(option) in fileDircionary">
<div :key="option.name">
<CCol sm="12">
<input type="checkbox" name="selectedFiles[]" :value="option.value" v-model="selectedFiles"
:id="option.value" :checked="selectedFiles.includes(option.value)"/> {{ option.label }}
</CCol>
</div>
</template>
<div>wybrano: {{ selectedFiles }}</div>
<CButton
color="primary"
@click="submit"
>
Zapisz
</CButton>
<CButton
color="success"
@click="goBack"
>
Cofnij
</CButton>
</div>
<div : id="tab2">Historia zmian</div>
<div : id="tab3">Pliki</div>
<div : id="tab4">
<CRow>
<CCol md="12">
<CTextarea
label="Uwagi do dostawy"
placeholder="Uwagi do dostawy"
rows="9"
v-model.trim="$v.form.deliver_comments.$model"
/>
</CCol>
</CRow>
<CRow>
<CCol md="12">
Podpis odbiorcy delivery_signature
</CCol>
</CRow>
<CButton
color="primary"
@click="submit"
>
Zapisz
</CButton>
<CButton
color="success"
@click="goBack"
>
Cofnij
</CButton>
</div>
</div>
</CForm>
<br/>
<div>
<CModal
title="Wybierz przewoźnika"
color="info"
:show.sync="warningModal"
size="xl"
:closeOnBackdrop=true
@closed="closedEvent"
:centered="true"
:onclose="closedEvent"
>
<data-table-select
:fetch-url="datatTableUrl"
:columns="['id', 'email', 'name', 'surname']"
:headers="{'id': 'ID','email': 'Email','name': 'Imię','surname': 'Nazwisko'}"
:routeName="routeName"
></data-table-select>
</CModal>
</div>
</CCol>
</CRow>
</CCardBody>
</CCard>
</template>
<script>
import axios from 'axios';
import Vue from 'vue';
import CKEditor from 'ckeditor4-vue';
import Swal from "sweetalert2";
import {validationMixin} from "vuelidate"
import {required, minLength, email, sameAs, helpers} from "vuelidate/lib/validators"
import Datepicker from 'vuejs-datepicker';
import {pl} from 'vuejs-datepicker/dist/locale'
import DataTableSelect from "../../components/DataTableSelect";
export default {
components: {
DataTableSelect,
Datepicker
},
data() {
return {
warningModal: false,
datatTableUrl: '',
routeName: 'carriers',
modalClasses: ['modal', 'fade'],
checkedNames: [],
pl: pl,
activeItem: 'home',
form: {
carrier_id: null,
carrier_name: null,
carrier_nip: null,
carrier_regon: null,
carrier_email: null,
carrier_postal_code: null,
carrier_street: null,
carrier_city: null,
carrier_email_to_notification: null,
driver_id: null,
comments: null,
transport_content: null,
deliver_comments: null,
carrier_phone: null,
},
confirmationTypes: [],
transportTypes: [],
drivers: [],
productDircionary: [],
documentDircionary: [],
fileDircionary: [],
selectedProducts: [],
selectedDocuments: [],
selectedFiles: [],
}
},
computed: {
formString() {
return JSON.stringify(this.form, null, 4)
},
isValid() {
return !this.$v.form.$invalid
},
isDirty() {
return this.$v.form.$anyDirty
},
},
mixins: [validationMixin],
validations: {
form: {
speed_number: {
required,
minLength: minLength(3),
maxLength: 255
},
...
}
},
methods: {
closedEvent(){
alert();
},
isActive(menuItem) {
return this.activeItem === menuItem
},
setActive(menuItem) {
this.activeItem = menuItem
},
goBack() {
this.$router.replace({path: '/tasks'})
},
checkIfValid(fieldName) {
const field = this.$v.form[fieldName]
if (!field.$dirty) {
return null
}
return !(field.$invalid || field.$model === '')
},
store() {
let self = this;
axios.post(this.$apiAdress '/api/tasks?token=' localStorage.getItem("api_token"),
{
carrier_id: self.form.carrier_id,
carrier_name: self.form.carrier_name,
carrier_nip: self.form.carrier_nip,
carrier_regon: self.form.carrier_regon,
carrier_email: self.form.carrier_email,
carrier_postal_code: self.form.carrier_postal_code,
carrier_street: self.form.carrier_street,
carrier_city: self.form.carrier_city,
carrier_email_to_notification: self.form.carrier_email_to_notification,
driver_id: self.form.driver_id,
comments: self.form.comments,
transport_content: self.form.transport_content,
deliver_comments: self.form.deliver_comments,
carrier_phone: self.form.carrier_phone,
selected_products: self.selectedProducts,
selected_documents: self.selectedDocuments,
selected_files: self.selectedFiles,
})
.then(function (response) {
if (response.data.status == 'success') {
Swal.fire(
'Sukces',
'Rekord dodany poprawnie!',
'success'
).then(function () {
// self.$router.push('/tasks');
});
} else {
Swal.fire(
'Błąd',
response,
'error'
)
}
}).catch(function (error) {
if (error.response.data.message != '') {
let errorDetails = ""
for (let key in error.response.data.errors) {
errorDetails = `${error.response.data.errors[key]}<br/>`
}
Swal.fire(
'Błąd',
errorDetails,
'error'
)
window.scrollTo({top: 0});
} else {
Swal.fire(
'Błąd',
error,
'error'
)
self.$router.push({path: 'login'});
}
});
},
submit() {
let self = this;
Swal.fire({
title: "Czy zapisać dane",
text: "zlecenia w bazie?",
type: "question",
showCancelButton: true,
confirmButtonText: "Tak, zapisz",
cancelButtonText: "Anuluj",
reverseButtons: true
}).then(function (result) {
if (result.value) {
self.runSubmit();
}
});
},
runSubmit() {
let self = this;
self.validate();
if (!this.$v.form.$invalid) {
this.store();
}
},
validate() {
this.$v.$touch()
},
reset() {
this.form = this.getEmptyForm()
this.submitted = false
this.$v.$reset()
},
getEmptyForm() {
return {
speed_number: null,
order_number: null,
data_start: null,
data_finish: null,
address_from: null,
address_to: null,
is_neutral_option: null,
transport_type: null,
carrier_id: null,
carrier_name: null,
carrier_nip: null,
carrier_regon: null,
carrier_email: null,
carrier_postal_code: null,
carrier_street: null,
carrier_city: null,
carrier_email_to_notification: null,
driver_id: null,
comments: null,
transport_content: null,
deliver_comments: null,
carrier_phone: null,
selected_products: null,
selectedProducts: [],
selectedDocuments: [],
selectedFiles: [],
}
}
},
created: function () {
this.datatTableUrl = Vue.prototype.$apiAdress '/api/tasks/carriers-table?token=' localStorage.getItem("api_token");
let self = this;
self.setActive('tab1');
axios.get(this.$apiAdress '/api/tasks/create?token=' localStorage.getItem("api_token"))
.then(function (response) {
self.confirmationTypes = response.data.confirmationTypes;
self.drivers = response.data.drivers;
self.transportTypes = response.data.transportTypes;
self.productDircionary = response.data.productDircionary;
self.documentDircionary = response.data.documentDircionary;
self.fileDircionary = response.data.fileDircionary;
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
}
}
</script>
and Datatable:
<template>
<div>
<div >
<div >
<div >
<input
v-model="search"
placeholder="Szukaj..."
type="text"
@keyup.enter="handleSearch"
>
<div >
<button type="button" @click.prevent="handleSearch">
<font-awesome-icon icon="fas fa-search"/>
</button>
</div>
</div>
</div>
<div >
<div >
<label for="pageOption" >Na stronie</label>
<select v-model="perPage" @change="handlePerPage" id="pageOption">
<option v-for="page in pageOptions" :key="page" :value="page">{{ page }}</option>
</select>
</div>
</div>
</div>
<table >
<thead>
<tr>
<th >#</th>
<th v-for="(label, column) in headers" :key="column" @click="sortByColumn(column)" >
{{ label | columnHead }}
<span v-if="column === sortedColumn">
<font-awesome-icon v-if="order === 'asc'" icon="fas fa-angle-up" />
<font-awesome-icon v-else icon="fas fa-angle-down" />
</span>
</th>
<th >Opcje</th>
</tr>
</thead>
<tbody>
<tr v-if="tableData.length === 0">
<td :colspan="columns.length 1">Brak danych do wyświetlenia.</td>
</tr>
<tr v-for="(data, key1) in tableData" :key="data.id" v-else>
<td>{{ serialNumber(key1) }}</td>
<td v-for="(value, key) in data" style="cursor: pointer">{{ value }}</td>
<td><button @click="prepareAddToTask(data.id)">Wybierz</button></td>
</tr>
</tbody>
</table>
<nav v-if="pagination && tableData.length > 0">
<ul >
<li :>
<a href="#" @click.prevent="changePage(currentPage - 1)">Poprzednia</a>
</li>
<li v-for="page in pagesNumber"
:>
<a href="javascript:void(0)" @click.prevent="changePage(page)" >{{ page }}</a>
</li>
<li :>
<a href="#" @click.prevent="changePage(currentPage 1)">Następna</a>
</li>
<span style="margin-top: 8px;"> <i>Wyświetlam {{ pagination.data.length }} z {{ pagination.meta.total }} wyników.</i></span>
</ul>
</nav>
</div>
</template>
<script type="text/ecmascript-6">
import axios from 'axios';
import Vue from 'vue';
import 'vuejs-datatable/dist/themes/bootstrap-4.esm';
import {
VuejsDatatableFactory,
IDataFnParams,
IDisplayHandlerParam,
ITableContentParam,
TColumnsDefinition,
VueDatatable
} from 'vuejs-datatable';
Vue.use(VuejsDatatableFactory, VueDatatable);
import Swal from 'sweetalert2';
export default {
props: {
fetchUrl: {type: String, required: true},
columns: {type: Array, required: true},
headers: {type: Object, required: true},
routeName: {type: String, required: true},
showUrl: {type: String, required: false},
},
data() {
return {
tableData: [],
url: '',
pagination: {
meta: {to: 1, from: 1}
},
offset: 4,
currentPage: 1,
perPage: 100,
sortedColumn: this.columns[0],
order: 'asc',
search: '',
pageOptions: [100, 200, 500, 1000],
selectedContent: ''
}
},
watch: {
fetchUrl: {
handler: function (fetchUrl) {
this.url = fetchUrl
},
immediate: true
}
},
created() {
console.log(this.fetchUrl);
return this.fetchData()
},
computed: {
/**
* Get the pages number array for displaying in the pagination.
* */
pagesNumber() {
if (!this.pagination.meta.to) {
return []
}
let from = this.pagination.meta.current_page - this.offset
if (from < 1) {
from = 1
}
let to = from (this.offset * 2)
if (to >= this.pagination.meta.last_page) {
to = this.pagination.meta.last_page
}
let pagesArray = []
for (let page = from; page <= to; page ) {
pagesArray.push(page)
}
return pagesArray
},
/**
* Get the total data displayed in the current page.
* */
totalData() {
return (this.pagination.meta.to - this.pagination.meta.from) 1
}
},
methods: {
fetchData() {
let dataFetchUrl = `${this.url}&page=${this.currentPage}&column=${this.sortedColumn}&order=${this.order}&per_page=${this.perPage}&search=${this.search}`
axios.get(dataFetchUrl)
.then(({data}) => {
this.pagination = data
this.tableData = data.data
}).catch(error => this.tableData = [])
},
/**
* Get the serial number.
* @param key
* */
serialNumber(key) {
return (this.currentPage - 1) * this.perPage 1 key
},
/**
* Change the page.
* @param pageNumber
*/
changePage(pageNumber) {
this.currentPage = pageNumber
this.fetchData()
},
/**
* Sort the data by column.
* */
sortByColumn(column) {
if (column === this.sortedColumn) {
this.order = (this.order === 'asc') ? 'desc' : 'asc'
} else {
this.sortedColumn = column
this.order = 'asc'
}
this.fetchData()
},
handleSearch() {
this.fetchData()
},
handlePerPage($event) {
this.perPage = $event.target.value;
this.fetchData()
},
recordLink(id) {
return this.routeName `/${id.toString()}`
},
showRecord(id) {
const recordLink = this.recordLink(id);
this.$router.push({path: recordLink});
},
getDataRecord(id) {
let self = this;
axios.get(this.$apiAdress '/api/carriers/' id '/edit?token=' localStorage.getItem("api_token"))
.then(function (response) {
self.selectedContent = response.data.record;
console.log(self.selectedContent); // here is my result @@
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
},
prepareAddToTask(id){
const copyId = id;
Swal.fire({
title: 'Czy na pewno',
text: "Chcesz dodać tego przewoźnika?",
type: 'question',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Tak!',
cancelButtonText: 'Anuluj',
buttonsStyling: true
}).then((isConfirm) => {
// now 'this' depends on the lexical enviroment.
if(isConfirm.value === true) {
this.getDataRecord(copyId);
}
});
}
///
},
filters: {
columnHead(value) {
return value.split('_').join(' ').toUpperCase()
}
},
name: 'DataTable'
}
</script>
I need show in console log in Main.VUE content which I get from modal / Datatable.
How can I make it?
CodePudding user response:
Since you are new to Vue.js I recommend you reading about props-down and events-up pattern
which describes flow of data between Vue components.
- Props-down part is describing data flow PARENT -> CHILD, meaning child components should receive data from parent components via props.
- Events-up part is describing data flow CHILD -> PARENT, meaning child components should send data to parent components by emitting events.
To get back to your concrete situation, you need to emit an event from Datatable.vue
component which you will handle in Main.vue
:
In your Datatable.vue
you should add this:
getDataRecord(id) {
let self = this;
axios.get(this.$apiAdress '/api/carriers/' id '/edit?token=' localStorage.getItem("api_token"))
.then(function (response) {
self.selectedContent = response.data.record;
self.$emit('content-selected', self.selectedContent); <-- add this line
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
},
Now, in your Main.vue
you need to handle the event like this:
<data-table-select
:fetch-url="datatTableUrl"
:columns="['id', 'email', 'name', 'surname']"
:headers="{'id': 'ID','email': 'Email','name': 'Imię','surname': 'Nazwisko'}"
:routeName="routeName"
@content-selected="handleContentSelected($event)" <-- add this line
></data-table-select>
Now, add your function that will handle the event, in your methods add:
handleContentSelected(content) {
console.log(content); <-- your data should be here
}