I have a list of orders which I am populating via the orders array. Each of these orders have a functionality where the user can edit these orders. I am trying to generate a vuetify dialog box component when the user clicks on the edit button which will have the default data from that order which the user can choose to edit. So far I have tried this,
<tbody class="items">
<tr v-for="order in orders" :key="order.name">
<td>{{ order.fullname }}</td>
<td>{{ order.address }}</td>
<td>{{ order.phone }}</td>
<!-- <td>{{ order.orderQuantity }}</td> -->
<td>{{ order.quantity }}</td>
<td v-if="order.status == true">
<v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
</td>
<td v-if="order.status == false">
<v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
</td>
<td>
<!-- <component v-bind:is="component"></component> -->
<!-- <EditOrder></EditOrder> -->
<v-dialog v-model="dialog" persistent max-width="290">
<template #activator="{ on, attrs }">
<v-btn icon color="primary" dark v-bind="attrs" v-on="on"> Open Dialog </v-btn>
</template>
<v-card>
<v-card-title class="headline"> Use Google's location service? </v-card-title>
<v-card-text>{{ phone }}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="dialog = false"> Disagree </v-btn>
<v-btn color="green darken-1" text #click="dialog = false"> Agree </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</td>
</tr>
</tbody>
but this generates n instances of the dialog box before the actual components loads. I also tried using async components but couldn't figure out where I was wrong.
This is what I think should happen;
When the user clicks on the "edit order" button, a dialog box component with the default order details for that particular order must be automatically generated and destroyed.
Please correct me if I am wrong.
Move the dialog part to a separate component
In the parent component, use it once at the beginning with dialog=false to be hidden initially
Add a data attribute orderToEdit which will be a prop to the child component
For each order in the list, add an edit button that would call a method when clicked passing the order
In this method, set orderToEdit to the passed order of the clicked item and set dialog=true to show it with the custom values
const dialogmodel = Vue.component('btn', {
template: '#dialogmodel',
props: { order: Object, value: Boolean },
computed: {
dialog: {
get () { return this.value; },
set (value) { this.$emit('close', value); }
}
}
});
new Vue({
el:"#app",
vuetify: new Vuetify(),
components: { dialogmodel },
data: () => ({
orders: [
{ name:"1", fullname:"fullname1", address:"address1", phone:"phone1", quantity:"quantity1", status:false },
{ name:"2", fullname:"fullname2", address:"address2", phone:"phone2", quantity:"quantity2", status:true }
],
dialog:false,
orderToEdit: {}
}),
methods: {
closeDialog(value) { this.dialog = value; },
editOrder(order) { this.orderToEdit = order; this.dialog = true; }
}
});
<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><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">
<template id="dialogmodel">
<div>
<v-dialog v-model="dialog" max-width="290" persistent>
<v-card>
<v-card-title class="headline">
{{ order.fullname }}
</v-card-title>
<v-card-text> {{ order.address }} </v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="$emit('close')">
Disagree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<v-app id="app">
<div>
<dialogmodel v-model="dialog" :order="orderToEdit" #close="closeDialog" />
</div>
<div>
<table>
<tbody class="items">
<tr v-for="order in orders" :key="order.name">
<td>{{ order.fullname }}</td>
<td>{{ order.address }}</td>
<td>{{ order.phone }}</td>
<td>{{ order.quantity }}</td>
<td v-if="order.status == true">
<v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
</td>
<td v-if="order.status == false">
<v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
</td>
<td>
<v-btn #click="editOrder(order)">Edit</v-btn>
</td>
</tr>
</tbody>
</table>
</div>
</v-app>
Are you looking to have a central popup being filled with the data of the order you selected to edit? In that case you can simply have a single component for the edit popup (instead of adding them to your v-for).
You can do it like this:
When clicking on 'edit', set a data property 'orderIndexToEdit' to the index of the order in the v-for
have a single dialog box that becomes visible when 'orderIndexToEdit' is truethy using v-if or v-show
Create a computed property that returns the correct order from the orders array, based on the index stored in the 'orderIndexToEdit' variable.
Use that computed property to populate the dialog box.
Let me know whether this helps!
Related
I'm trying to display custom text in v-select options by slots.
Template:
<v-select v-model="dLevelSelected" :items="dLevels" solo>
<template slot="item" slot-scope="data">
<span>{{data.description}}</span>
</template>
</v-select>
Script:
data () {
return {
dLevelSelected: null,
dLevels: [{id:1, value: 1, description: 'Level1'}, {id:2, value: 2, description: 'Level2'}]
}
}
With this, when you open the v-select the two registers of dLevels are appearing as boxes but with any text. It seems like the data.description is being evaluated like data.undefined
Thanks!
slot and slot-scope are deprecated as of Vue 2.6.0.
The new slot syntax combines those two props into v-slot, so the equivalent item slot is:
<template v-slot:item="scope">
<span>{{ scope.item.description }}</span>
</template>
Note the scope contains an item property that can be used to access description. You can use object destructuring to simplify that to:
<template v-slot:item="{ item }">
<span>{{ item.description }}</span>
</template>
Similarly, you'll probably want a custom selection slot that renders the appearance of the selected item:
<template v-slot:selection="{ item }">
<span>{{ item.description }} ({{ item.value }})</span>
</template>
The final template should look similar to this:
<v-select v-model="dLevelSelected" :items="dLevels" solo>
<template v-slot:item="{ item }">
<span>{{ item.description }}</span>
</template>
<template v-slot:selection="{ item }">
<span>{{ item.description }} ({{ item.value }})</span>
</template>
</v-select>
demo
So I have a v-menu with some options. Now I want to display the selected option in another part of my app (same component). I tried to do it using v-model but it doesn't work. What's the best way to do it?
This is the code for the v-menu and where I want to display the selected option:
<v-menu bottom transition="scale-transition" >
<v-btn slot="activator">
28
</v-btn>
<v-list>
<v-list-tile
v-for="(item, index) in PIFBitems"
:key="index"
v-model="hasan"
#click="boardSwitch('shoPFIB', index)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
.
.
.
<p class="chipPam13"
>{{this.hasan}}</p>
.
.
This is the script code:
data() {
return {
hasan:'',
PIFBitems:[
{title: empty},
{title: PIFB}
]
}
}
Please use hasan rather than this.hasan in your HTML:
<p class="chipPam13">{{hasan}}</p>
Or if v-model doesn't work, you can try to set hasan value at boardSwitch function:
...
methods: {
boardSwitch (firstArg, secondArg, value) {
...
this.hasan = value
},
...
}
Please don't forget to add third argument to your function call in HTML:
<v-list-tile
v-for="(item, index) in PIFBitems"
:key="index"
v-model="hasan"
#click="boardSwitch('shoPFIB', index, item.title)"
>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
I have a 5 text fields.
1._id,
2.name,
3.display,
4.reference,
5.ref_id
I want to enable only 2nd ,3rd & 4th text fields on a button click. So, I am declaring a variable 'disable' in data section of Vue.js, and calling function enableFields() on button click event. Here's my template code:
<template>
<div>
<!-- Dialog Modal Design -->
<v-dialog v-model="dialog" #keydown.esc="setDialog(false)" width="800px">
<v-card>
<v-card-title
class="grey lighten-4 py-4 title">
<v-icon>fa-envelope-open</v-icon>
Add/Edit a Record
</v-card-title>
<!-- Modal pop up text fields -->
<v-container grid-list-sm class="pa-4">
<v-layout row wrap>
<v-flex xs12 align-center justify-space-between>
<v-layout align-center
v-for="(column, i) in columns"
:key ="i"
v-if="column.field != ''">
<v-text-field
v-bind:value="getEntryFieldData(column)"
:label="column.headerName"
:disabled="disable">
</v-text-field>
<!-- ="(column.headerName == '_id')" -->
</v-layout>
</v-flex>
</v-layout>
</v-container>
<!-- Edit/Update/Cancel Buttons -->
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="secondary" #click="onCancel">
<v-icon>fa-ban</v-icon>
Cancel
</v-btn>
<v-btn color="primary" #click="onCancel">
<v-icon>fa-save</v-icon>
Update
</v-btn>
<v-btn v-if="visible"
color="primary" #click="enableFields">
<v-icon>fa-pencil-alt</v-icon>
Edit
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
And here's my script:
<script>
import {mapGetters} from 'vuex';
export default {
name: 'MasterModal',
props: {
input: Object,
addBtnClick: Boolean
},
data () {
return {
isReadOnly: false,
dialog: false,
valid: false,
visible: true,
disable: true
};
},
computed: {
...mapGetters('masterData', {
entryState: 'entryState',
entryData: 'entryData',
columns: 'columns'
})
},
watch: {
addBtnClick: function (newValue, oldValue) {
this.setDialog(!this.dialog);
}
},
methods: {
setDialog (bValue) {
this.dialog = bValue;
},
// Called when the cancel button is pressed in the form
// Clears and data currently entered in the form and closes the input modal
onCancel () {
this.setDialog(false);
},
// Reading all column values and filling row data into the textbox in the v-for of template
getEntryFieldData (column) {
return this.entryData[column.field];
},
enableFields () {
this.disable = false;
}
}
};
</script>
Basically I am getting confused about assigning property of each text field
as I am generating them dynamically using v-for.
The fastest way to achieve it is this:
<v-text-field
v-bind:value="getEntryFieldData(column)"
:label="column.headerName"
:disabled="disable || i == 0 || i == 4">
</v-text-field>
So adding,
In HTML
:disabled="setDisable(column.field)" works for me.
In Script
setDisable (colName) {
return this.entryState === 'read' || colName.toLowerCase().indexOf('id') !== -1;
}
works for me. Basically I am checking which column.field text has id and disabling it by checking its index.
I have a code segment that i have mentioned below. I have used vuejs with vuetifyjs.
<v-data-table v-bind:headers="headers" v-bind:items="uniqueSubjects">
<template slot="items" slot-scope="props">
<td class="text-xs-center">{{ props.index+1 }}</td>
<td class="text-xs-center">{{ month[new Date(props.item.daily_date).getMonth()] }},{{ props.item.student_id }}</td>
<td class="text-xs-center">{{ props.item.subjects.subject_name }}{{ props.item.subjects.id }}</td>
<template v-for="n in 31">
<template v-for="(mark,index) in resultsList">
<template v-if="new Date(mark.daily_date).getDate() == n && mark.subject_id == props.item.subject_id && mark.student_id == props.item.student_id">
<td class="text-xs-center">
{{ mark.daily_marks }}
</td>
</template>
<template v-else-if="index>=resultsList.length-1">
<td class="text-xs-center">-</td>
</template>
</template>
</template>
</template>
<template slot="pageText" slot-scope="{ pageStart, pageStop }">
From {{ pageStart }} to {{ pageStop }}
</template>
</v-data-table>
I want to break my <template v-for="(mark,index) in resultsList"> loop when the internal v-if condition is true.
Here i want to search the inner loop from first to last.I want to break the loop if the data is matched depending on the given condition.and then i want to do it again and again.
How can I do that? Any help would be appreciated.
Thanks in advance.
There is no way to break v-for. It is better to create a computed property in your component and filter your data there to pass only needed data to v-for.
For example:
// ... your component code
computed: {
validResultsList() {
return this.resultsList/* ... your filtering logic ... */;
}
}
// ... your component code
And then in your template:
<template v-for="(mark,index) in validResultsList">
What i am trying to achieve is to select a file and read its contents in a new dialog box.
I have it working until the point i try and select the same file as the previous loaded one, selecting a different file works perfectly.
I have tried clearing the file input which appears to work but it still doesn't select the file again.
See my example below, any help much appreciated!
Codepen
new Vue({
el: "#app",
data: () => ({
importedData: null,
dialogImport: false
}),
watch: {
importedData: {
handler() {
this.checkData();
},
deep: true
}
},
methods: {
openFileExplorer() {
this.$refs.fileInputImport.value = "";
this.$refs.fileInputImport.click();
},
selectFile(e) {
const self = this;
const file = e.target.files[0];
let reader = new FileReader();
reader.onload = e => {
self.importedData = reader.result;
self.checkedData = true;
};
reader.readAsText(file);
},
checkData() {
// check the file
this.dialogImport = true;
}
}
});
<link href="https://unpkg.com/vuetify#0.17.4/dist/vuetify.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet" />
<div id="app">
<v-app id="inspire">
<div class="text-xs-center">
<input ref="fileInputImport" type="file" name="fileInputImport" #change="selectFile">
<v-btn color="primary" #click.stop="openFileExplorer()">Choose file</v-btn>
<v-dialog v-model="dialogImport" fullscreen transition="dialog-bottom-transition" :overlay="false" scrollable>
<v-container class="ma-0 pa-0 white" style="max-width:100%">
<v-layout row wrap justify-left>
<v-card style="width:100%;">
<v-toolbar class="amber lighten-1 elevation-0">
<v-toolbar-title>Imported data</v-toolbar-title>
<v-spacer></v-spacer>
<div>
<v-flex xs12>
<v-container fluid>
<v-layout row align-center justify-center>
<v-flex>
<v-tooltip bottom close-delay="0">
<v-btn icon slot="activator" #click.native="dialogImport = false">
<v-icon>close</v-icon>
</v-btn>
<span>Close</span>
</v-tooltip>
</v-flex>
</v-layout>
</v-container>
</v-flex>
</div>
</v-toolbar>
<div>{{ importedData }}</div>
</v-card>
</v-layout>
</v-container>
</v-dialog>
</div>
</v-app>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.17.4/dist/vuetify.min.js"></script>
I don't think watch is required. Here is the updated codepen
It was able to read the text and assigns it to importedData. But the importedData has the same content when you select same file, watch is not tried the this.checkData().
Either you reset importedData when your close the dialog or remove the watch as i did.