I'm working on a drag and drop component in Vue but when I try to access the form using this.$refs, it is returning undefined. I'm using the dialog UI component from Vuetify to place my upload form into a dialog/modal.
CodePen
The dialog is a child component to another component, and the form isn't visible until after the "Add Attachments" button is clicked. I suspect the later is the issue, but I'm unsure how to solve it. I figured placing the code under the mounted life-cycle hook would do the trick but I understand that runs immediately when it renders the button into the parent component.
<template>
<v-dialog v-model="dialog" persistent max-width="600px" style="z-index:999;">
<template v-slot:activator="{ on }">
<v-btn small outlined color="#102a43" v-on="on">Add Attachments</v-btn>
</template>
<v-card>
<v-card-text class="pt-4">
<v-container class="my-4">
<form ref="fileform" class="file-upload-form">
<div v-if="dragAndDropCapable" class="dropzone">
<p>
Drop your file here, or
<span>browse</span>
</p>
<p>Supported File Types: pdf, jpg, png</p>
</div>
<div v-else>
<v-file-input label="Select File" outlined dense></v-file-input>
</div>
</form>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="#b2b2b2" text #click="cancel">Cancel</v-btn>
<v-btn color="#102a43" outlined>Upload</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
data: function() {
return {
files: [],
dialog: false,
dragAndDropCapable: false
};
},
methods: {
isDragAndDropCapable() {
const div = document.createElement("div");
return (
("draggable" in div || ("ondragstart" in div && "ondrop" in div)) &&
"FormData" in window &&
"FileReader" in window
);
},
cancel() {
this.dialog = false;
}
},
mounted() {
//Verify Drag and Drop Capability
this.dragAndDropCapable = this.isDragAndDropCapable();
//Code below return undefined - Expected behavior is to return form element
console.log(this.$refs.fileform);
}
};
</script>
<style lang="scss" scoped>
.dropzone {
border: 2px dashed #90a4ae;
border-radius: 8px;
min-height: 5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
&:hover {
cursor: pointer;
}
p {
margin-bottom: 0;
&:first-of-type {
font-weight: 500;
font-size: 1rem;
color: #263238;
span {
color: #62b0e8;
}
}
&:last-of-type {
font-size: 0.8rem;
}
}
}
</style>
By default content within v-dialog components isn't rendered until it is activated.
Add the eager attribute to change this <v-dialog eager>
https://vuetifyjs.com/en/components/dialogs/
On mounted, the v-dialog isnt actually mounted or lets say its not initialized.
I have modified your CodePen.
Try this eager prop of the v-dialog like this below :
<v-app id="app">
<v-app id="inspire">
<v-dialog v-model="dialog" persistent max-width="600px" eager>
<template v-slot:activator="{ on }">
<v-btn color="red lighten-2"
dark
v-on="on">Add Attachments</v-btn>
</template>
<v-card>
<v-card-text class="pt-4">
<v-container class="my-4">
<form ref="fileform" class="file-upload-form">
<div v-if="dragAndDropCapable" class="dropzone">
<p>
Drop your file here, or
<span>browse</span>
</p>
<p>Supported File Types: pdf, jpg, png</p>
</div>
<div v-else>
<v-file-input label="Select File" outlined dense></v-file-input>
</div>
</form>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="#b2b2b2" text #click="cancel">Cancel</v-btn>
<v-btn color="#102a43" outlined>Upload</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app>
</div>
Related
I am working with vuetify and I want to implement a Stepper.
Within the stepper I want to have v-card which once clicked on read more would open. So far the functions are working.
The problem is that when I only open a card - meaning in the index.vue I put the card component instead of the stepper, the card opens reacts perfectly. But, when the card is within the stepper component, when I press on read more it is only shown a part of it. I guess some element is having a fixed size and wont resize, but I cannot figure it out where exactly the problem is. Any help is welcomed!
My index.vue file looks like this:
<template>
<v-container>
<v-app>
<div>
<Stepper/>
</div>
</v-app>
</v-container>
</template>
<script>
import Stepper from "../components/Stepper.vue";
export default {
data: () => ({
}),
components: {Stepper }
}
</script>
The Stepper.vue component looks like this:
<template>
<v-stepper v-model="e6" vertical ref="stepper">
<!--- Step 1-->
<v-stepper-step :complete="e6 > 1" step="1">
</v-stepper-step>
<v-stepper-content step="1">
<Card1 />
</v-stepper-content>
<!--- Step 2-->
<v-stepper-step :complete="e6 > 2" step="2">
</v-stepper-step>
<v-stepper-content step="2">
<Card2 />
</v-stepper-content>
<!--- Step 3-->
<v-stepper-step :complete="e6 > 3" step="3">
</v-stepper-step>
<v-stepper-content step="3">
<Card2 />
</v-stepper-content>
</v-stepper>
</template>
The cards inside are from format identical only the text is different. Hence I will post only one card:
<template>
<v-hover >
<template v-slot:default="{ hover }">
<v-card class="mx-auto">
<v-img class="white--text" src="picture1.png" width="300" height="169">
<v-card-title class="text-h5">
{{ $t('welcome_hello') }}
</v-card-title>
</v-img>
<v-card-actions>
<v-btn text color="teal accent-4" #click="openCard()">
Learn More
</v-btn>
</v-card-actions>
<v-expand-transition>
<v-card v-if="reveal" class="transition-fast-in-fast-out v-card--reveal" style="height: 100%;">
<v-card-text class="pb-0">
<p class="text-h4 text--primary">
{{ $t('welcome_message1') }}
</p>
<p>{{ $t('welcome_message2') }}</p>
<p>{{ $t('welcome_message3') }}</p>
</v-card-text>
<v-card-actions class="pt-0">
<v-btn text color="teal accent-4" #click="nextStep()">
Next
</v-btn>
</v-card-actions>
</v-card>
</v-expand-transition>
</v-card>
</template>
</v-hover>
</template>
<script>
export default {
data: () => ({
reveal: false,
}),
methods: {
openCard() {
this.reveal = true
},
closeCard() {
this.reveal = false
},
completedCard(){
},
nextStep() {
this.closeCard()
this.$root.$emit('nextStepFunction') //like this
},
}
}
</script>
<style>
.v-card--reveal {
bottom: 0;
opacity: 1 !important;
position: absolute;
width: 100%;
}
</style>
You should give your v-card a height and then overflow to scroll.
<v-card height="450" style="overflow-y: scroll; overflow-x: hidden">
// Your card body
</v-card>
I have a Nuxt.js and Vue.js project and I have a layout in it that all my landing pages use this layout and I have given a series of styles to the v-app-bar menu tag in this layout. But as you can see in the image on the brokers page, the styles are displayed correctly for the nav-bar section , This means that the nav-bar menu buttons are white in color,
But on the how it work page, the color of the menus should not be white and should be changed to black so that the menus can be easily viewed.
How it works page:
Brokers page:
The main How it works page, which should be designed in the same way:
My Code is:
<v-app-bar absolute color="transparent" elevate-on-scroll>
<div class="d-flex align-center">
<v-img max-width="166"lass="d-flex align-center mr-10" src="/RectangleProfile.png">
</v-img>
<v-btn nuxt to="/landing-page/how-it-works" text
class="mr-2 font-bold-link-text">
How it Works
</v-btn>
<v-btn
text
class="mr-2 font-bold-link-text"
nuxt
to="/landing-page/list-of-top-brokers"
>Brokers</v-btn >
<v-btn text class="mr-2 font-bold-link-text">Tutorial</v-btn>
<v-btn text class="mr-2 font-bold-link-text">Blogs</v-btn>
<v-btn text class="mr-2 font-bold-link-text">About us</v-btn>
<v-btn
v-if="selectedMagnify === false" icon
v-on:click.stop="searchSelected()">
<v-icon color="white">
mdi-magnify
</v-icon>
</v-btn>
<v-text-field
v-else
flat
color="#ffffff"
autofocus
hide-details
rounded
height="40"
filled
dense
single-line
clearable
placeholder="Search">
</v-text-field>
</div>
</v-app-bar>
My CSS Code is:
.font-bold-link-text {
font-weight: normal;
font-size: 14px;
color: #ffffff !important;
}
As Iman said in the comments, you can use a scoped style on the page where you have the problem.
Otherwise, another way is to create a dark class that is bind to a var. And you set it to true on the page where you need the black buttons. That way, you can have your style in a global stylesheet.
Something like :
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<v-btn text class="mr-2 font-bold-link-text">Light button</v-btn>
<v-btn text class="mr-2 font-bold-link-text" :class="dark: dark">Dark button</v-btn>
<script>
export default {
data(){
return{
dark: true,
}
},
}
</script>
<style scoped>
.font-bold-link-text{
padding: 8px 16px;
color: black;
background: white;
}
.dark{
padding: 8px 16px;
color: white;
background: black;
}
</style>
<style>
#import url("https://fonts.googleapis.com/css2?family=Creepster&display=swap");
#title {
font-family: "Creepster", cursive;
font-weight: 300;
font-size: 1.5rem;
}
</style>
<template>
<v-app>
<v-app-bar color="#FFEE58" app>
<v-app-bar-nav-icon>
<v-img alt="Heart Logo" width="60" contains src="#/assets/logo.png"/>
</v-app-bar-nav-icon>
<v-spacer></v-spacer>
<div id="title">
<v-toolbar-title>
<b>Title</b>
</v-toolbar-title>
</div>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>mdi-heart</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-magnify</v-icon>
</v-btn>
<v-menu left bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item v-for="n in 5" :key="n" #click="() => {}">
<v-list-item-title>Option {{ n }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-main>
<router-view/>
</v-main>
</v-app>
</template>
I've tried wrapping v-toolbar-title in h6. then adding h6 the #title section as well, but the title is still not changing size. I've also tried using px, %, and rem for the font size.
I assume that there is probably a size limit inside of an app-toolbar, but no matter which size I choose, it's still not changing.
You can create your own class and apply it to the <b> tag wrapping the title:
<b class="mytitle">Title</b>
.mytitle {
font-size: 1.5rem !important;
}
Or use the existing toolbar title class and apply it to all toolbar titles:
.v-toolbar__title {
font-size: 1.5rem !important;
}
The !important modifier is necessary
Although #Dan answer would work, I'd recommend not to use !important rule without a particular need. You can achieve the same behavior by providing a more "strong" CSS selector. The easiest way to do it in your case would be to introduce scoped styles for that component.
Title
<style scoped>
.mytitle {
font-size: 1.5rem;
}
</style>
I made a table component in my project as I have multiple tables that I want to all look the same.
I insert my table onto my page and everything is working fine.
On the table is a dialog which opens correctly but inside that dialog is another one of my table components and this does not render.
It will work if I change the name of the component and have two separate instances but that is not what I am trying to do.
How can I get my table working across all components? Using Vue CLI.
Table component:
<template>
<v-data-table :headers="headers" :items="items" :no-data-text="noDataText" :dark="dark">
<template v-slot:top>
<v-toolbar :dark="dark" flat>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-btn #click="dialog=true" color="success" class="mr-2">
Load Default Frames
<v-icon right>mdi-download</v-icon>
</v-btn>
<v-btn color="primary" class="mr-2">
Create New Frame
<v-icon right>mdi-image-plus</v-icon>
</v-btn>
<Dialog v-model="dialog" />
<!-- <Frame v-model="dialog" :editedFrame="editedFrame" :oldIndex="oldIndex" #close="close" />
<DefaultFrames v-model="defaultFramesDialog" :selectable="true" :items="defaultFrames" />-->
</v-toolbar>
</template>
</v-data-table>
</template>
Dialog component:
<template>
<v-dialog v-model="dialog">
<v-card :dark="dark">
<v-card-title>
{{ name}}
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-btn icon #click="dialog = false">
<!-- <v-icon #click="$emit('close')">mdi-close</v-icon> -->
</v-btn>
</v-card-title>
<Table :is="child_component" :headers="frameHeaders" :items="defaultFrames" />
<v-card-actions>
<v-btn #click="log">Log</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
Table.vue
<template>
<v-btn #click.stop="emitOpenDialog">Open Dialog</v-btn>
</template>
<script>
export default {
methods: {
emitOpenDialog() {
// I've use vueBus for emiting
this.$bus.emit("open-dialog")
}
}
}
</script>
Dialog.vue
<template>
<v-dialog v-model="dialog"> ... </v-dialog>
</template>
<script>
export default {
data: () => ({
dialog: false
},
created() {
this.$bus.on("open-dialog", this.openDialog)
},
beforeUnmount() {
this.$bus.off("open-dialog")
},
methods: {
openDialog() {
this.dialog = true
}
}
}
I am building a task manager app using Vue.js, Vuetify, and Firebase. Clicking "Add new note" opens a Vuetify dialog box which prompts the user to input data. Clicking save closes the dialog box while simultaneously submitting and rendering the inputted data to a task card on the screen. The rendered task card includes a "view/edit" button which, when clicked, opens a second dialog box for the user to view and edit data. My issue is with editing. I currently have the "view/edit" button set up with an #click event that triggers the modal. I need this view/edit #click event to also trigger a function that binds the data in a selected task card to the second dialog box, which can then be edited. I attempted to achieve this by setting up the #click event in the "view/edit" button like so:
<v-btn color="primary" dark #click.stop="dialogUpdate = true; editTodo(todo)">
View/Edit
</v-btn>
...as you can see, the #click event contains "dialog = true", which is used to toggle the Vuetify dialog box, and "editTodo(todo)", which triggers the editTodo function that binds the inputted data to the second dialog box. This currently works fine, but my issue is that I am not clear on whether or not I should actually be adding two functions to one click event. Other Stack Overflow posts that I have researched suggest that this is not a good practice. If this is wrong, then how would you recommend configuring the "view/edit" button so that opening the second dialog box also triggers editTodo function? My full component is below. Thank you!
<template>
<div id="app" data-app>
<v-layout justify-center>
<v-btn color="primary" dark #click.stop="dialog = true">
Add New Note
</v-btn>
<v-dialog v-model="dialog" max-width="290">
<v-card color="#f9efaf">
<v-form #submit.prevent="addTodo">
<v-card-text>
<textarea-autosize v-model="newTitle" :min-height="50" placeholder="add title"></textarea-autosize>
<textarea-autosize v-model="newTodo" type="text" style="width: 100%" :min-height="100" placeholder="add note"></textarea-autosize>
</v-card-text>
<v-btn type="submit" color="green darken-1" text #click="dialog = false">
Add
</v-btn>
</v-form>
<v-card-actions>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="dialogUpdate" max-width="290">
<v-card color="#f9efaf">
<v-form #submit.prevent="updateTodoText">
<v-card-text>
<textarea-autosize v-model="todoEditTitle" :min-height="50" placeholder="add title"></textarea-autosize>
<textarea-autosize v-model="todoEditText" type="text" :min-height="100" placeholder="add note"></textarea-autosize>
</v-card-text>
<v-btn type="submit" color="green darken-1" text #click="dialogUpdate = false">
Update
</v-btn>
</v-form>
<v-card-actions>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
<v-container>
<v-flex md12 class="elevation-0">
<v-layout wrap>
<v-flex md3 v-for="todo in todos" :key="todo.id">
<v-card color="#f9efaf" class="card-container">
<textarea-autosize v-model="todo.title" class="todo-text" readonly style="width: 60%"></textarea-autosize>
<textarea-autosize v-model="todo.text" class="todo-text" readonly></textarea-autosize>
<v-btn #click="deleteTodo(todo.id)">Delete</v-btn>
<v-btn color="primary" dark #click.stop="dialogUpdate = true; editTodo(todo)">
View/Edit
</v-btn>
</v-card>
</v-flex>
</v-layout>
</v-flex>
</v-container>
</div>
</template>
<script>
import { todosCollection } from './firebase';
import { mapState } from 'vuex'
export default {
name: 'app',
created() {
this.getData();
},
data () {
return {
todos: [],
newTitle: '',
newTodo: '',
currentlyEditing: null,
todoEditTitle: '',
todoEditText: '',
dialog: false,
dialogUpdate: false
}
},
methods: {
getData(){
const todos = []
todosCollection.orderBy('createdAt').get()
.then(snapshot => {
snapshot.forEach(doc => {
let userData = doc.data()
userData.id = doc.id
todos.push(userData)
})
this.todos = todos
})
},
addTodo() {
todosCollection.add({
title: this.newTitle,
text: this.newTodo,
createdAt: new Date()
})
.then(() => {
this.newTitle = '',
this.newTodo = ''
})
},
deleteTodo(id) {
todosCollection.doc(id).delete()
.then(() => {
console.log('Document successfully deleted')
})
.then(() => {
this.getData()
})
},
editTodo(todo) {
this.currentlyEditing = todo.id
this.todoEditText = todo.text
this.todoEditTitle = todo.title
},
updateTodoText() {
todosCollection.doc(this.currentlyEditing).update({
text: this.todoEditText,
title: this.todoEditTitle
})
.then(() => {
this.getData()
})
.catch(function(error) {
console.error("Error updating document text: ", error);
});
this.currentlyEditing = null;
this.todoEditText = '';
this.todoEditTitle = '';
}
}
}
</script>
<style>
body {
margin: 0;
padding: 0;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin: 0;
padding: 0;
}
.card-container {
margin: 10px;
padding: 10px;
}
</style>
SOLUTION:
<v-btn color="primary" dark #click.stop="editTodo(todo)">
View/Edit
</v-btn>
editTodo(todo) {
this.dialogUpdate = true
this.currentlyEditing = todo.id
this.todoEditText = todo.text
this.todoEditTitle = todo.title
},
This is the solution. dialogUpdate = true is supposed to be wrapped inside the editTodo() function, along with code used to bind the inputted data to the second dialog box.
<v-btn color="primary" dark #click.stop="editTodo(todo)">
View/Edit
</v-btn>
editTodo(todo) {
this.dialogUpdate = true
this.currentlyEditing = todo.id
this.todoEditText = todo.text
this.todoEditTitle = todo.title
},