I have a number (size) field that can range from 0 to 7. I also have an intensity object that has 2 fields (in and out) I would like to display the number of objects in relation to the number in the number +1 field
What method can I use to solve my problem ?
I made a JSFiddle if someone have a solution for me .
https://jsfiddle.net/sebastianczech/2wfapuv4/61/
I also need, when I check the 'In = Out' checkbox, to attribute to 'Out' the same value than 'In'.
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<div id="app">
<v-app >
<h1>Selection</h1>
<div v-for="car in cars">
<v-select
label="Selection"
:items="car.version"
item-text="color"
>
</v-select>
<v-text-field
label="Size"
type="number"
min="1"
max="8"
v-model="sizeValue">
</v-text-field>
<div v-for="(int, index) in intensity">
<v-text-field
label="In"
type="number"
min="1"
max="100"
v-model="int.in">
</v-text-field>
<v-text-field
label="Out"
type="number"
min="1"
max="100"
v-model="int.out">
</v-text-field>
</div>
</div>
<v-checkbox label="Apply to All" v-model="checkboxAll"></v-checkbox>
<v-checkbox label="In = Out" v-model="checkboxInOut"></v-checkbox>
</v-app>
</div>
Vue.use(Vuetify);
var vm = new Vue({
data() {
return {
checkboxInOut: false,
checkboxAll: false,
sizeValue: null,
intensity:[
{in: void 0, out:void 0}],
cars: [
{
name: 'McQueen',
version: [
{color: 'blue' },
{color: 'red' },
{color: 'green' },
{color: 'purple' },
]
}
],
}
},
methods: {
}
})
Added fixes to the above code
working codepen here: https://codepen.io/chansv/pen/KKKvrom?editors=1010
<div id="app">
<v-app id="inspire">
<v-container fluid>
<h1>Selection</h1>
<div v-for="car in cars">
<v-select
label="Selection"
:items="car.version"
item-text="color"
>
</v-select>
<v-btn #click="addIntensity">
add intensity
</v-btn>
<br>
<br>
</v-text-field>
<div v-for="(int, index) in intensity">
index{{index}}
<v-btn #click="delIntensity(index)">
delete intensity
</v-btn>
<v-text-field
label="In"
type="number"
min="1"
max="100"
v-model="int.in">
</v-text-field>
<v-text-field
label="Out"
type="number"
min="1"
max="100"
v-model="int.out">
</v-text-field>
</div>
</div>
<v-checkbox label="Apply to All" v-model="checkboxAll"></v-checkbox>
<v-checkbox label="In = Out" v-model="checkboxInOut"></v-checkbox>
</v-container>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
checkboxInOut: false,
checkboxAll: false,
sizeValue: 0,
intensity:[
{in: 0, out: 0}],
cars: [
{
name: 'McQueen',
version: [
{color: 'blue' },
{color: 'red' },
{color: 'green' },
{color: 'purple' },
]
}
],
}),
methods: {
addIntensity() {
this.$set(this.intensity, this.intensity.length, {in: 0, out: 0});
},
delIntensity(index) {
this.intensity.splice(index, 1);
}
},
created() {
for(var i = 0; i <= val;i++){
this.$set(this.intensity, i, {in: 0, out: 0});
}
},
watch: {
checkboxInOut(val) {
if (val) {
this.intensity.forEach(x => {
x.out = x.in;
});
}
}
}
})
Related
I want to make the second checkbox always selected by default even on page reload like the image-
The user can select other checkboxes later but by default, the second should be selected always.
Here is the checkbox code-
<template>
<div>
<div class="tw-flex tw-flex-wrap">
<div class="tw-w-full lg:tw-w-1/4">
<collabmed-loading v-if="!initialised">Loading Patient Info...</collabmed-loading>
<reception-patient-info
class="tw-px-0 tw-pt-0"
v-else
:patient-id="admissionRequest.patient_id"
>
</reception-patient-info>
</div>
<div class="tw-w-full lg:tw-w-3/4 lg:tw-pl-5">
<acemed-card>
<acemed-title>
Check In Details
<template #right>
<div v-if="initialised" class="tw-text-lg tw-text-gray-800"><strong>Admission Type:</strong> {{ admissionRequest.admission_type_name }}</div>
</template>
</acemed-title>
<div>
<collabmed-loading v-if="!initialised"></collabmed-loading>
<div v-else>
<div class="tw-flex tw-flex-wrap">
<div class="tw-w-full lg:tw-w-1/2">
<div class="tw-flex tw-flex-wrap tw-justify-between">
<div>
<v-tooltip bottom v-if="msetting('inpatient.inpatient_no_prefix')">
<v-btn flat style="min-width: 50px" slot="activator" class="pull-right pt-3">
{{ msetting('inpatient.inpatient_no_prefix') }}-
</v-btn>
<span>IP Number Prefix</span>
</v-tooltip>
</div>
<div>
<v-text-field
v-model="obj.admission.inpatient_no"
class="mr-2"
label="IP Number"
hint="Possible Format: #######/YY"
></v-text-field>
</div>
<div>
<v-tooltip bottom>
<v-btn
color="primary"
style="min-width: 50px"
slot="activator"
#click="generateIpNumber()"
:loading="generating"
>
<v-icon small>mdi-cached</v-icon>
</v-btn>
<span>Generate IP Number</span>
</v-tooltip>
</div>
</div>
</div>
<div class="tw-w-full tw-pt-5 lg:tw-pt-0 lg:tw-w-1/2 lg:tw-pl-5">
<collabmed-date-time-picker
label="Admission Date"
outline
:maxDate="today"
#input="setAdmissionDate"
></collabmed-date-time-picker>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5">
<div v-if="! obj.admission.external_doctor">
<div v-if="! obj.admission.doctor_id">
<users-search
:roles-like="['doc']"
:label="'Admission Doctor'"
#results="setDoctor"
></users-search>
</div>
<p v-else class="pt-3 pl-3 border-bottom subheading">Doctor:</p>
</div>
<p v-else class="pt-3 pl-3 border-bottom subheading">External Doctor:</p>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5 lg:tw-pl-5">
<div v-if="obj.admission.doctor_id">
<p class="pt-3 pl-3 border-bottom subheading">
{{ obj.admission.doctor_name }}
<v-btn small color="red" icon dark #click="unsetDoctor()">
<v-icon small>delete</v-icon>
</v-btn>
</p>
</div>
<div v-else>
<v-text-field
v-model="obj.admission.external_doctor"
class="mx-2"
label="External Doctor"
hide-details clearable
></v-text-field>
</div>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5">
<v-autocomplete
v-model="obj.admission.ward_id"
:items="wards"
#change="updateBeds()"
item-text="name"
item-value="id"
label="Select a Ward"
hide-details
outline
></v-autocomplete>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5 lg:tw-pl-5">
<div v-if="wardCashCharge" class="pl-3">
<p class="font-weight-bold title">Ward Charges</p>
<p>Cash Charge: <strong>{{ wardCashCharge | numberFormat }}</strong></p>
<p>Insurance Charge: <strong>{{ wardInsuranceCharge | numberFormat }}</strong></p>
</div>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5">
<v-autocomplete
v-model="obj.admission.bed_id"
:disabled="! obj.admission.ward_id"
:items="beds"
item-text="name"
item-value="id"
label="Select a Bed"
hide-details
outline
></v-autocomplete>
<p v-if="overbookingAllowed"><em>Over-booking of beds has been allowed in the system</em></p>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5 lg:tw-pl-5">
<v-autocomplete
v-model="obj.admission.charges"
:disabled="! obj.admission.ward_id"
:items="charges"
item-text="name"
item-value="id"
label="Select Other Charge(s)"
class="ml-2"
hide-details multiple
outline chips
></v-autocomplete>
</div>
<div class="tw-w-full lg:tw-w-1/2 tw-pt-5">
<v-alert :value="true" v-if="errors.any()" type="error" outline>
<div v-html="errors.display()"></div>
</v-alert>
</div>
<div class="tw-w-full tw-pt-5">
<v-btn block dark color="primary" class="mt-4" :loading="saveLoader" #click="save()">
Admit Patient
<v-icon class="pl-2">arrow_right_alt</v-icon>
</v-btn>
</div>
</div>
</div>
</div>
</acemed-card>
</div>
</div>
</div>
</template>
export default {
props: {
admissionRequestId: {
required: true,
}
},
data() {
return {
admissionRequestObj: new AdmissionRequest(),
obj: new Admission(),
wards: null,
saveLoader: false,
beds: [],
charges: [],
wardCashCharge: null,
wardInsuranceCharge: null,
today: moment(new Date()).format('YYYY-MM-DD HH:MM:ss'),
overbookingAllowed: false,
generating: false,
}
},
computed: {
...mapGetters([
'getWards',
]),
admissionRequest() {
return this.admissionRequestObj.selected
},
initialised() {
return this.wards && this.admissionRequest
},
saved() {
return this.obj.saved
},
submitted() {
return this.obj.form.submitted
},
contaminated() {
return this.obj.form.errorDetected;
},
errors() {
return this.obj.form.errors;
},
generatedIpNuber() {
return this.obj.generatedIpNuber
}
},
watch: {
contaminated(val) {
if(val) {
this.saveLoader = false
}
},
submitted(val) {
if(val) {
this.saveLoader = false
}
},
saved(val) {
if(val) {
this.saveLoader = false
this.dialog = false
window.location = route('inpatient.admissions.index').relative()
}
},
admissionRequest(val) {
if(val) {
this.obj.admission.admission_request_id = val.id
this.obj.admission.visit_id = val.visit_id
this.obj.admission.inpatient_no = val.visit.patient.inpatient_no
this.obj.admission.patient_id = val.visit.patient.id
}
},
getWards(val) {
if(val) {
this.wards = this.getWards.data
}
},
generatedIpNuber(val) {
this.generating = false
if(val)
this.obj.admission.inpatient_no = val
}
},
methods: {
...mapActions([
'setWards',
]),
initialise() {
this.admissionRequestObj.find(this.admissionRequestId)
this.setWards()
this.obj.admission.admission_date = this.today
this.overbookingAllowed = this.$options.methods.msetting('inpatient.allow_overbooking_of_wards') == 1
},
updateBeds() {
let ward = _.find(this.wards, {id: this.obj.admission.ward_id})
this.wardCashCharge = ward.cash_cost
this.wardInsuranceCharge = ward.insurance_cost
this.beds = _.map(ward.beds, item => {
let name = "Bed No. " + item.number
! item.is_available ? name += " (Unavailable)" : '';
let disabled = ! item.is_available
this.overbookingAllowed ? disabled = false : null
return {
name: name,
id: item.id,
disabled: disabled
}
})
let firstAvailableBed = _.find(this.beds, { disabled: false })
firstAvailableBed ? this.obj.admission.bed_id = firstAvailableBed.id : null
if(this.charges && this.charges.length && this.charges.length >=2) {
// charges[1] = second item
this.obj.admission.charges.push(this.charges[1].id)
}
this.charges = _.map(ward.charges, item => {
let name = item.name + " - Kshs. " + item.cost;
item.type == 'recurring' ? name += " [RECURRING]" : '';
return {
id: item.id,
name: name
}
})
},
setDoctor(doctor) {
this.obj.admission.doctor_id = doctor.id
this.obj.admission.doctor_name = doctor.full_name
},
unsetDoctor() {
this.obj.admission.doctor_id = null
this.obj.admission.doctor_name = null
},
generateIpNumber() {
this.generating = true
this.obj.generateInpatientNumber()
},
setAdmissionDate(datetime) {
this.obj.admission.admission_date = datetime
},
save() {
this.saveLoader = true
this.obj.save()
}
},
mounted() {
this.initialise()
}
}
</script>
<style scoped lang="scss">
</style>
The api data looks like-
When I select bed, I call a method to fetch the charges. Thats where I have placed the code to select the admission charge where second charge item should always be selected.
If you only want to keep the second item by default selected, then on the mounted hook, just find the second item's id in your charges array, and push this id to your v-model variable (obj.admission.charges) and you should see that the second item is always selected.
Here is the demo-
NOTE-
I used your API data inside the data property of Vue as I can't send requests to your server. I also assumed the possible structure of obj.admission because you didn't mention it.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<v-app>
<v-container>
<v-autocomplete
v-model="obj.admission.charges"
:disabled="!obj.admission.ward_id"
:items="charges"
item-text="name"
item-value="id"
label="Select Other Charge(s)"
class="ml-2"
hide-details
multiple
outlined
chips
>
</v-autocomplete>
</v-container>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
search: null,
obj: {
admission: {
charges: [],
ward_id: "something",
}
},
charges: [
{
id: 2,
name: "Nursing Fee",
cost: "3000.00",
type: "recurring",
created_at: "2022-08-23 08:00",
},
{
id: 31,
name: "Admission Fee",
cost: "once",
type: "3000.00",
created_at: "2022-08-23 08:00",
},
{
id: 32,
name: "Dr. inpatient visit",
cost: "3000.00",
type: "once",
created_at: "2022-08-23 08:00",
},
{
id: 36,
name: "Dr. Anne Masicka",
cost: "3000.00",
type: "once",
created_at: "2022-08-23 08:00",
},
{
id: 29,
name: "Dr. inpatient visit (Dr. Marwa)",
cost: "3000.00",
type: "once",
created_at: "2022-08-23 08:00",
}
]
}
},
mounted() {
if(this.charges && this.charges.length && this.charges.length >=2) {
// charges[1] = second item
this.obj.admission.charges.push(this.charges[1].id)
}
}
})
</script>
</body>
</html>
I have a prop and currently am able to get the data of the prop, Am trying a way to capture the item of the prop when saving the form.
Is there a way where i can take the value and pass if in a hidden text-area and bind the data to the vmodel?
Any help I appreciate.
<v-dialog v-model="dialog" persistent max-width="800">
<template v-slot:activator="{ on }">
<v-btn dark v-on="on" color="primary" round> Make payment </v-btn>
</template>
<v-card>
<v-card-title class="headline primary">
<span class="white--text">Add a new Doctor Payment Record {{ queueId }}</span>
<v-btn icon dark #click.native="dialog = false" absolute right>
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<users-search
:leave-selected="true"
idOnly
label="Select Doctor"
#results="setDoctor"
>
</users-search>
<div class="row px-3">
<v-autocomplete
class="px-3 col-sm-8"
v-model="expense.bank"
v-if="banks.data"
:items="banks.data"
outline
chips
label="Select bank"
item-text="name"
item-value="id"
>
</v-autocomplete>
<v-text-field
class="px-3 col-sm-8"
outline
flat
v-model="expense.amount"
type="number"
#input="expense.percentage()"
required
label="Amount *"
persistent-hint
/>
</div>
<v-text-field
class="px-3"
outline
flat
v-model="expense.total_paid"
required
label="amount paid"
persistent-hint
/>
<v-text-field
class="px-3"
outline
flat
:value="setQueue"
v-model="expense.queueId"
required
:label=queueId
persistent-hint
/>
<v-alert :value="true" type="error" v-if="errors.any()">
<div v-html="errors.display()"></div>
</v-alert>
<v-layout row wrap>
<v-flex xs12>
<v-btn
color="success"
:loading="saveLoader"
#click="recordExpense()"
>save</v-btn
>
</v-flex>
</v-layout>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script>
import NewUser from "#finance/libs/users/NewUser";
import {mapActions, mapGetters} from "vuex";
export default {
props: [
'queueId'
],
data: () => ({
dialog: false,
expense: new NewUser(),
saveLoader: false,
}),
computed: {
...mapGetters({
banks: "getBanks",
}),
balance: function () {
return parseFloat(10);
},
submitted() {
return this.expense.form.submitted;
},
contaminated() {
return this.expense.form.errorDetected;
},
errors() {
return this.expense.form.errors;
},
},
watch: {
submitted(v) {
if (v) {
this.saveLoader = false;
}
},
contaminated() {
this.saveLoader = false;
},
},
methods: {
...mapActions({
fetchBanks: "setBanks",
}),
setDoctor(user) {
this.expense.doctor_id = user.id;
},
setQueue(){
console.log(this.queueId);
this.expense.queueId = this.queueId;
},
async recordExpense() {
this.saveLoader = true;
let response = await this.expense.saveExpense();
this.saveLoader = false;
if (response) {
this.dialog = false;
this.$emit("expenseCreated");
}
},
},
mounted() {
this.fetchBanks();
}
};
</script>
The prop queueId i also want to store it along with the user information from the form.
Try this one, it should work:
<template>
<textarea v-model="hiddenValue" :style="{ display: 'none' }"></textarea>
</template>
<script>
export default {
props: [ 'queueId' ],
data() {
return {
hiddenValue: this.queueId
}
}
}
</script>
In case you will no need the prop to be modified, please bind the texarea value to the prop directly:
<textarea hidden v-model="queueId" :style="{ display: 'none' }></textarea>
I have these combobox chips with a prob deletable-chips
<v-combobox
v-model="selectedCategories"
:items="attributeCategories"
item-text="name"
item-value="id"
label="Category"
multiple
chips
clear-icon="mdi-close-circle"
deletable-chips
v-on:change="changeCategory(selectedCategories)"
></v-combobox>
Is there a way to prevent a specific chip to be deleted? For example not show the remove button on a specific one? Let's say for Device and only allow Weather and Geo Location to me removed
Instead of using in-built delete method of v-chips. You can do the implementation via custom #click:close event. I created a working demo for you :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
model: [],
items: [
{
text: 'Weather'
},
{
text: 'Geo Location'
},
{
text: 'Device'
}
]
}),
methods: {
remove (itemText) {
if (itemText === 'Device') {
return;
} else {
this.model.forEach(obj => {
if (obj.text === itemText) {
this.model.splice(this.model.indexOf(obj), 1)
}
})
this.model = [...this.model]
}
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://unpkg.com/#mdi/font#6.x/css/materialdesignicons.min.css"/>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-combobox
v-model="model"
:items="items"
label="Select"
multiple
small-chips
>
<template v-slot:selection="{ attrs, item, parent, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close="false"
#click:close="remove(item.text)"
>
<span class="pr-2">
{{ item.text }}
</span>
</v-chip>
</template>
</v-combobox>
</v-container>
</v-app>
</div>
I'm passing an array to a component but the component sees the array as undefined. Here is the parent calling the component...
<FileList ref="files" class="ma-3 pa-0" :passFiles="true" :passedFiles="header.files"></FileList>
Vue devtools sees the array, it is valid. As seen in the screenshot below:
Yet in my created hook in the controller, it shows this.passedFiles as undefined. (this.passFiles, however, shows correctly as true.)
created(){
console.log(this.passFiles,this.passedFiles); //this.passedFiles shows as undefined
window.addEventListener("resize", this.onResize);
},
I dumped the array right before it gets sent to the component, and it is there, see screenshot:
I tried this just to make sure, and it gets passed to the array fine:
:passedFiles="[{0: '1'}]"
I'm pulling my hair out here. Here is the full component, it is long but it shows you everything
<template>
<div>
<div class="text-center pa-10" v-show="loading">
<v-progress-circular
:size="35"
:width="3"
color="primary"
indeterminate
></v-progress-circular>
</div>
<v-data-table
v-show="!loading"
:headers="showActions ? headers : headersRead"
:items="orderedFiles"
:items-per-page="paginate"
:footer-props="{'items-per-page-options':[paginate, 15, 30, 50, 100, -1]}"
:hide-default-footer="oneFileOnly"
class="elevation-1 custom-rounded-box ma-0 pa-0"
ref="aWidth"
:style="forceHeight&&$vuetify.breakpoint.mdAndUp ? 'height:'+forceHeight+'px;' : ''"
>
<template slot="no-data">
<div>There are currently no files here</div>
</template>
<template v-slot:item.description="{item, index}">
<v-row
no-gutters
style="flex-wrap: nowrap;"
>
<v-col
cols="12"
md="11"
class="flex-grow-0 flex-shrink-0"
>
<v-tooltip bottom v-if="item.description">
<template v-slot:activator="{ on, attrs }">
<a
v-if="item.gdoc"
style="text-decoration: none; color: orange;"
v-bind="attrs"
v-on="on"
#click.prevent="gdocDialog = true;editingFile = item"
class="d-block text-truncate"
:style="$vuetify.breakpoint.mdAndUp ? 'max-width:'+aWidth+'px;' : 'max-width:'+bWidth+'px;'"
>
{{item.description}}
</a>
<a
v-else
:href="'/getFile?id='+item.id"
style="text-decoration: none; color: orange;"
v-bind="attrs"
v-on="on"
class="d-block text-truncate"
:style="$vuetify.breakpoint.mdAndUp ? 'max-width:'+aWidth+'px;' : 'max-width:'+bWidth+'px;'"
>
{{item.description}}
</a>
</template>
<span>{{item.file_name_original}}</span>
</v-tooltip>
<div v-else>
<a
v-if="item.gdoc"
style="text-decoration: none; color: orange;"
#click.prevent="gdocDialog = true;editingFile = item"
class="d-block text-truncate"
:style="$vuetify.breakpoint.mdAndUp ? 'max-width:'+aWidth+'px;' : 'max-width:'+bWidth+'px;'"
>
{{item.file_name_original}}
</a>
<a
v-else
:href="'/getFile?id='+item.id"
style="text-decoration: none; color: orange;"
class="d-block text-truncate"
:style="$vuetify.breakpoint.mdAndUp ? 'max-width:'+aWidth+'px;' : 'max-width:'+bWidth+'px;'"
>
{{item.file_name_original}}
</a>
</div>
</v-col>
<v-col
cols="12"
md="1"
style="min-width: 30px; max-width: 30px;"
class="flex-grow-1 flex-shrink-0"
v-show="$vuetify.breakpoint.mdAndUp"
>
<v-edit-dialog
:return-value.sync="item.description"
#save="editFileInline()"
#open="inlineEditOpen(item, index)"
v-if="showActions"
>
<template v-slot:input>
<v-text-field
ref="inline_file"
v-model="editingFile.description"
label="Edit"
single-line
counter
></v-text-field>
</template>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-icon
small
class="ml-2"
color="orange"
v-bind="attrs"
v-on="on"
width="100%"
>
mdi-pencil
</v-icon>
</template>
<span>Edit the file description</span>
</v-tooltip>
</v-edit-dialog>
</v-col>
</v-row>
</template>
<template v-slot:item.icon="{ item }">
<v-icon
:color="item.icon_color"
>
{{item.icon}}
</v-icon>
</template>
<template v-slot:item.uploaded="{ item }">
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs"
v-on="on">
{{item.date_difference}}
</div>
</template>
<span>
<v-avatar
size="26px"
class="mr-2"
>
<img
:src="'/img/profile-pictures/'+item.user.profile_photo_thumb"
>
</v-avatar>
{{item.pretty_date}} by {{item.user.full_name}}</span>
</v-tooltip>
</template>
<template v-slot:item.actions="{item}" v-if="showActions">
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-icon
small
color="red"
#click="showDeleteDialog(item)"
v-bind="attrs"
v-on="on"
>
mdi-delete
</v-icon>
</template>
<span>Delete</span>
</v-tooltip>
</template>
</v-data-table>
<!-- Upload modal -->
<v-dialog
v-model="fileUploadDialog"
max-width="500px"
width="500px"
:transition="transitionSiteWide()"
persistent
v-if="showActions"
>
<v-card>
<v-progress-linear
indeterminate
color="yellow darken-2"
v-show="fileUploadProcess"
></v-progress-linear>
<v-toolbar
dark
class="primary"
dense
elevation="0"
>
<v-icon class="mr-2">mdi-cloud-upload</v-icon>
<v-toolbar-title class="text">Upload File(s)</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-card-text class="pt-3" v-show="!fileUploadGDoc">
<template>
<v-file-input
small-chips
:label="!oneFileOnly ? 'Upload multiple files by clicking here' : 'Click here to upload a file'"
type="file"
ref="files"
:accept="acceptedFiles()"
#change="onFilePicked()"
:key="componentKey"
show-size
counter
:multiple="!oneFileOnly"
:rules="!oneFileOnly ? rules : rulesSingle"
></v-file-input>
</template>
</v-card-text>
<v-card-text class="pt-3" v-show="fileUploadGDoc">
<v-text-field
ref="gdoc_description"
v-model="gdoc_description"
label="Description"
:rules="gdoc_description_rules"
prepend-icon="mdi-pencil"
></v-text-field>
<v-text-field
ref="gdoc_link"
v-model="gdoc_link"
label="Link to your Google Document"
:rules="gdoc"
prepend-icon="mdi-google-drive"
></v-text-field>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text
#click="ok"
>
Close
</v-btn>
<v-btn
class="primary"
text
v-show="fileUploadButton"
#click="uploadFiles()"
:loading="fileUploadProcess"
>
Upload
</v-btn>
<v-btn
class="primary"
text
v-show="gdocValidated()"
#click="uploadFiles()"
:loading="fileUploadProcess"
>
Attach
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Delete dialog -->
<v-dialog
v-if="showActions"
v-model="deleteFileConfirm"
max-width="400px"
:transition="transitionSiteWide()"
>
<v-card elevation="0">
<v-progress-linear
indeterminate
color="yellow darken-2"
v-show="deleteFileLoading"
></v-progress-linear>
<v-toolbar
dark
class="primary"
dense
elevation="0"
>
<v-icon class="mr-2">mdi-text-box-minus</v-icon>
<v-toolbar-title class="text">{{editingFile.description ? editingFile.description : editingFile.file_name_original}}</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-card-text class="pb-0">
<v-container>
<p>Are you sure you want to delete this file?</p>
<p>{{editingFile.description ? editingFile.description : editingFile.file_name_original}}
will be removed from the system.</p>
</v-container>
</v-card-text>
<v-card-actions>
<v-btn
text
#click="deleteFileConfirm = false"
>
Close
</v-btn>
<v-spacer></v-spacer>
<v-btn
class="primary"
text
#click="deleteSet()"
>
Yes, delete this file
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Gdoc dialog -->
<v-dialog
v-model="gdocDialog"
max-width="1000px"
width="100%"
:transition="transitionSiteWide()"
>
<v-card elevation="0">
<v-toolbar
dark
color="teal"
dense
elevation="0"
>
<v-icon class="mr-2">mdi-google-drive</v-icon>
<v-toolbar-title class="text">{{editingFile.description ? editingFile.description : editingFile.file_name_original}}</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-card-text class="pa-0">
<iframe ref="gdocIframe" :src="editingFile.file_name_original" :style="'height:'+iframeHeight+'px;width:100%;border:0'"></iframe>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
text
#click="gdocDialog = false"
>
Close
</v-btn>
<v-btn
class="primary"
text
link
#click="openGdoc(editingFile.file_name_original);"
>
Open in new window
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
props: {
type: String,
id: [Number, String],
paginate: Number,
fileUploadDialog: Boolean,
fileUploadGDoc: Boolean,
ok: Function,
showActions: Boolean,
forceHeight: { type: Number, default: 0 },
oneFileOnly: Boolean,
passFiles: Boolean,
passedFiles: Array,
},
data () {
return {
headers: [
{
text: '',
align: 'start',
sortable: false,
value: 'icon',
width: '20px'
},
{ text: 'Description', value: 'description', sortable: false, },
{ text: 'Uploaded', value: 'uploaded', width: '150px' },
{ text: 'Actions', value: 'actions', sortable: false, width: '80px', align: 'center' },
],
headersRead: [
{
text: '',
align: 'start',
sortable: false,
value: 'icon',
width: '20px'
},
{ text: 'Description', value: 'description', sortable: false, },
{ text: 'Uploaded', value: 'uploaded', width: '150px' },
],
rules: [
files => !files || !files.some(file => file.size > 20_097_152) || 'Each file cannot exceed 20mb',
],
rulesSingle: [
],
gdoc: [
(value) => !!value || "Required",
(value) => this.isURL(value) || "URL is not valid",
],
gdoc_description_rules: [
(value) => !!value || "Required",
],
files: [],
fileUploadButton: false,
deleteFileConfirmLoading: false,
fileUpload: false,
fileUploadProcess: false,
componentKey: 0,
postFormData: new FormData(),
editingFile: {
description: null,
index: null,
file_name_original: null,
},
deleteFileConfirm : false,
deleteFileLoading: false,
gdoc_link: null,
gdoc_description: null,
gdocDialog: false,
iframeHeight: 0,
aWidth: 0,
loading: true,
}
},
computed:{
orderedFiles: function () {
return _.orderBy(this.files, 'created_at', 'desc')
},
},
watch: {
files: {
immediate: false,
handler(){
if(this.files){
this.total = this.files.length;
this.$emit('totalFiles', this.total);
}else{
this.total = 0;
}
},
},
id: {
immediate: false,
handler(){
this.getFiles()
},
}
},
methods: {
async getFiles(){
this.loading = true;
await axios
.get('/app/files/getFiles?id=' + this.id + '&type=' + this.type)
.then(response => {
if (response.data.length) {
this.files = response.data;
this.$emit('totalFiles', this.files.length);
this.resize();
this.loading = false;
} else {
this.files = [];
this.$emit('totalFiles', 0);
this.loading = false;
}
})
.catch(error => {
this.majorError();
})
.finally()
},
onFilePicked(e) {
this.postFormData = new FormData();
if(this.$refs.files.validate()){
this.fileUploadButton = true;
}
this.postFormData = new FormData();
for(let key in event.target.files){
if(key >= 0){
this.postFormData.append( 'files[]', event.target.files[key]);
}
}
},
async uploadFiles(){
this.fileUploadProcess = true;
this.postFormData.append('type', this.type);
this.postFormData.append('id', this.id);
this.postFormData.append('gdoc', this.fileUploadGDoc);
this.postFormData.append('gdoc_link', this.gdoc_link);
this.postFormData.append('gdoc_description', this.gdoc_description);
const res = await this.callApi('post', '/app/files/uploadFiles', this.postFormData);
if(res.status===200){
this.componentKey++; //reset trick
this.snackbar(res.data.msg,res.data.type);
this.ok();
this.fileUploadProcess = false;
this.gdoc_link = null;
this.gdoc_description = null;
this.$refs.gdoc_link.reset()
this.$refs.gdoc_description.reset()
if(res.data.files){
for (const file of res.data.files) {
this.files.push(file);
}
}
this.resize();
this.fileUploadButton = false;
}else{
this.fileUploadProcess = false;
this.snackbar(res.data.msg, res.data.type);
}
},
inlineEditOpen (item) {
let obj = { ...item, editingIndex: this.files.indexOf(item) }
this.editingFile = obj;
},
async editFileInline(){
const file = Object.assign({}, this.editingFile); //Turn array into object for laravel
const res = await this.callApi('post', '/app/files/updateFile',
{file: file});
if(res.status===201){
this.files[this.editingFile.editingIndex].description = this.editingFile.description;
this.snackbar(this.editingFile.description + " has been edited successfully", 'success');
this.resize();
}else{
if(res.status===422){
for(let i in res.data.errors) {
this.snackbar(res.data.errors[i][0], 'error');
}
}else{
this.snackbar("There has been an error, we don't have any more information for you", 'error');
}
}
},
showDeleteDialog(file){
this.deleteFileConfirm = true;
let obj = { ...file, index: this.files.indexOf(file)}
this.editingFile= obj;
},
async deleteSet(){
this.deleteFileLoading = true;
const res = await this.callApi('post', '/app/files/deleteFile', this.editingFile);
if(res.status===200){
this.files.splice(this.editingFile.index, 1);
this.snackbar("File deleted successfully", 'success');
this.deleteFileConfirm = false;
}else{
if(res.status===422){
this.snackbar(res.data.msg, 'error');
}
}
this.deleteFileLoading = false;
},
gdocValidated(){
if(this.gdoc_link&&this.$refs.gdoc_link.validate()&&this.gdoc_description){
return true;
}
},
openGdoc(url){
window.open(url, '_blank').focus();
},
onResize() {
this.iframeHeight = window.innerHeight - 220;
if(this.showActions){
this.aWidth = this.$refs.aWidth.$el.clientWidth - 355;
this.bWidth = this.$refs.aWidth.$el.clientWidth - 150;
}else{
this.aWidth = this.$refs.aWidth.$el.clientWidth - 270;
this.bWidth = this.$refs.aWidth.$el.clientWidth - 65;
}
},
resize(){
setTimeout(() => window.dispatchEvent(new Event('resize')), 1);
},
},
async mounted(){
if(this.passFiles){
this.files = this.passedFiles;
//console.log(this.passedFiles,this.files)
this.loading = false;
}else{
this.getFiles();
}
this.onResize();
this.resize();
},
created(){
console.log(this.passFiles,this.passedFiles); //this.passedFiles shows as undefined
window.addEventListener("resize", this.onResize);
},
destroyed(){
window.removeEventListener("resize", this.onResize);
this.editingFile = null;
},
}
</script>
What am I missing here?
new Vue({
el: '#app',
vuetify: new Vuetify({
theme: {
dark: true
},
}),
data() {
return {
loading: null,
requests: [{"req_id":78},{"req_id":79}],
tableHeaders: [
{
text: 'ID',
value: 'req_id',
align: 'center',
},
{ text: 'Actions', value: 'action', sortable: false, align: 'center'},
],
createloading: false,
cancelloading: false,
};
},
methods: {
createPacks(item) {
this.loading = !this.loading;
item.createloading = true;
},
excludeRequest(item) {
this.loading =!this.loading;
item.createloading = false;
},
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<div id="app">
<v-app>
<v-row no-gutters class="align-start justify-start">
<v-col>
<v-row>
<v-data-table
:headers="tableHeaders"
:items="requests"
:loading="loading"
>
<template v-slot:item.action="{ item }">
<v-btn color="success" #click="createPacks(item)" :loading="item.createloading" :disabled="item.createloading">Create</v-btn>
<v-btn color="error" #click="excludeRequest(item)" :loading="item.cancelloading" :disabled="!item.createloading">Cancel</v-btn>
</template>
</v-data-table>
</v-row>
</v-col>
</v-row>
</v-app>
</div>
Please help me implement this code correctly, as it does not function correctly. The buttons should correctly show the loading indicator and should be independent.
Thank you in advance for your generosity.
Below is a link to codepen.
https://codepen.io/protemir/pen/MWwGaeO