I'm experimenting with vue for the first time.
I've replaced a jquery show/hide that was using .slideDown() / .slideUp() with v-show - however I much prefer the animation of jQuery's slideup/down. Is there an easy way to do this with vue?
Simplified code here:
<nav class="bg-blue" role="navigation">
<div class="container classes here">
<div class="classes here">
<h1 class="classes here">
<a href="/" class="classes here">Site Name
</a>
</h1>
<button class="classes here" #click="isShowing ^= true">
hamburger svg here
</button>
</div>
<div class="main-nav classes here" v-show="!isShowing">
<div class="classes here">
<!-- nav items here -->
</div>
</div>
</div><!-- /.container -->
</nav>
Please advise.
You can install modules for this, or you can write a custom component. I suggest the second option.
<template>
<div>
<button #click="isShowing">
hamburger svg here
</button>
<!-- animation of appearance/disappearance.
More info: https://v2.vuejs.org/v2/guide/transitions.html -->
<!-- The attribute "name" is used to create custom classes
that are applied during animation -->
<transition name="main-nav"
#enter="transitionStep1"
#after-enter="transitionStep2"
#before-leave="transitionStep3"
#after-leave="transitionStep4">
<div class="main-nav" v-show="!active">
<div class="classes here">Some text</div>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "Accordion",
data() {
return {
// set the variable that will hide/show the block
active: false
}
},
methods: {
isShowing() {
// change the value of the variable that will hide/show the block
this.active = !this.active;
},
transitionStep1(el) {
// set the block height at the moment of its appearance
el.style.height = el.scrollHeight + 'px'
},
transitionStep2(el) {
// remove inline styles from the block after animation of its appearance
el.style.height = ''
},
transitionStep3(el) {
// set the height of the block at the beginning of its disappearance animation
el.style.height = el.scrollHeight + 'px'
},
transitionStep4(el) {
// remove inline styles from the block after the animation of its disappearance
el.style.height = ''
},
},
}
</script>
<style lang="scss" scoped>
.main-nav {
overflow: hidden;
-webkit-transition: height 0.3s ease;
transition: height 0.3s ease;
}
.main-nav-enter {
height: 0;
}
.main-nav-leave-to {
height: 0 !important;
}
</style>
Related
Template:
<div class="row">
<div class="col-lg-12 mt-2">
<draggable
class="list-group"
tag="ul"
v-model="imgList.top"
v-bind="dragOptions"
>
<transition-group type="transition">
<li
class="list-group-item" style="align-items: center;display: flex;padding: 12px 15px; font-size: 13px"
v-for="record in imgList.top"
:key="record._id"
#click="onClickValue"
<!-- #change="onChangeValue" -->
>
<div class="col-lg-12">
<img class="w-100" :src="getImageURL(record.image)" alt />
</div>
</li>
</transition-group>
</draggable>
</div>
</div>
<script>
import draggable from "vuedraggable";
export default {
components: {
draggable
},
data() {
return {
imgList: {
top: [
{_id: '12354356444433', image: '09jgg24.jpg'},
{_id: '12354356442211', image: '09jaef2.jpg'},
]
}
}
},
methods: {
onClickValue() {
console.log('ok') // No results received
},
//onChangeValue() {
// console.log('ok') // No results received
//},
}
}
</script>
I am doing drag-and-drop in vuejs. I hold the image and drag and drop it.. Now I want when I drag an image to catch the #click or #change event. I tried putting #click and #change in my code. But when I drag and drop nothing appears? Please show me to catch that event. Thank you
I have a modal rendered on top of a semi-transparent backdrop. Both elements have a v-if controlled by the same variable.
Although the enter transition animation works fine, the `leave`` transition animation is ignored (it should fade out smoothly, instead it disappears instantly). Why?
Codepen
Markup:
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</div>
CSS:
.content-enter-active {
animation: slide-up .75s;
}
.content-leave-active {
animation: fade-out .75s;
}
#keyframes slide-up {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
#keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
It seems that the div with modalBackdrop class is disappearing before the nested div with class modalContent does its transition, so try to wrap modal Backdrop by a transition component with name backdrop which also takes the fade-out animation :
.backdrop-leave-active,.content-leave-active { /*.backdrop-leave-active is sufficient since the parent opacity is applied on children*/
animation: fade-out .75s;
}
template :
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<transition name="backdrop" appear>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</transition>
</div>
DEMO
When showModal is false, the transition element is destroyed immediately. If the only reason you use v-if="showModal" in transition parent is to disable modalBackdrop, then you can assign this class dynamically.
This is working as expected:
<div :class="{ modalBackdrop: showModal }">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
I have this issue I've been hitting for hours now; I can't understand why it doesn't work as expected.
I pasted an example code below. The issue is that when editing the name, {{name}} is not updated. However, if I remove either of the <transition> element or the v-if="show" condition, then data binding works as expected. Same if the {{name}} is placed outside the transition.
So it seems the transition blocks data binding? However I don't find anything about it in the docs or elsewere. I tested this code in a Vue2 playground, and it works as expected (data binding works). So the behavior seems to depend on Vue3.
Is there something I'm missing? Is it a bug in Vue3?
Thanks in advance for any input or idea.
<template>
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<div v-if="show">
<p>hello, {{name}}</p>
<input v-model="name" type="text" />
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
data() {
return {
name: "",
show: true,
}
}
});
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.8s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
It works just fine in plain JS...
So try to focus on the differences:
TypeScript (i cannot use it here on SO) - I really doubt its the cause but you can try
Scoped CSS - did you tried to remove scoped ? There are some issues with scoped CSS and <transition>. Check this issue in Vue-loader. My example is not build with Webpack so Vue-loader is not used but it's for sure used in your project...
const app = Vue.createApp({
data() {
return {
name: "",
show: true,
}
},
template: `
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<div v-if="show">
<p>hello, {{name}}</p>
<input v-model="name" type="text" />
</div>
</transition>
</div>
`
}).mount("#app");
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.8s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app"></div>
I meet same question, you can try to set the initial value of 'show' to false and at the lifeCycle to modify 'show' for true.
Goal: I want to be able to have a modal template that I can extend in other pages in my Vue.js (Nuxt.js) application
ModalTemplate.vue:
<!-- Base Modal Component -->
<template>
<!-- Modal -->
<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center">
<div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50"></div>
<!-- Modal Container -->
<div class="modal-container bg-gray-300 w-5/12 mx-auto rounded shadow-lg z-50 overflow-y-auto">
<!-- Top Right escape button (needs to be within the container for z-index purposes) -->
<div class="modal-close absolute top-0 right-0 cursor-pointer flex flex-col items-center mt-4 mr-4 text-white text-sm z-50">
<fa icon="times" class="fa-2x"></fa>
<span class="text-sm">(Esc)</span>
</div>
<div class="modal-content">
<!-- Title of Modal -->
<div class="modal-title-container">
<slot name="modal-header"></slot>
</div>
<!-- Body of Modal -->
<div class="modal-body-container">
<slot></slot>
</div>
<!-- Footer of Modal -->
<div class="modal-footer-container">
<slot name="modal-footer"></slot>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ModalTemplate',
data() {
return {
}
},
methods: {
toggleModal: function() {
var body = document.querySelector('body');
var modal = document.querySelector('.modal');
modal.classList.toggle('opacity-0');
modal.classList.toggle('pointer-events-none');
body.classList.toggle('modal-active');
},
modalHidden: function () {
this.toggleModal();
this.messageBus.$emit('closing')
},
keyDownPressed: function(keyPressed) {
var isEscape = false
if (keyPressed.key === "Escape" || keyPressed.key === "Esc") {
isEscape = true
} else {
isEscape = (keyPressed.keyCode === 27)
}
if (isEscape && document.querySelector('body').classList.contains('modal-active')) {
this.modalHidden();
}
}
},
created: function() {
window.addEventListener('keydown', this.keyDownPressed)
},
destroyed: function() {
window.removeEventListener('keydown', this.keyDownPressed)
},
mounted: function() {
this.toggleModal();
var closeModalSelector = document.querySelectorAll('.modal-close')
for (var i = 0; i < closeModalSelector.length; i++) {
closeModalSelector[i].addEventListener('click', this.modalHidden)
}
const overlay = document.querySelector('.modal-overlay')
overlay.addEventListener('click', this.modalHidden);
}
}
</script>
<style lang="postcss">
.modal-page {
#apply pointer-events-none;
#apply fixed;
#apply w-full;
#apply h-full;
#apply top-0;
#apply left-0;
#apply flex;
#apply items-center;
#apply justify-center;
}
.modal-overlay {
#apply absolute;
#apply w-full;
#apply h-full;
#apply bg-gray-900;
}
.modal-container {
#apply bg-gray-300;
#apply mx-auto;
#apply rounded;
#apply shadow-lg;
#apply z-50;
#apply overflow-y-auto;
}
.modal-content {
#apply py-4;
}
.modal-title-container {
#apply flex;
#apply justify-between;
#apply items-center;
#apply border-b;
#apply border-gray-400;
#apply px-4;
#apply pb-3;
}
.modal-body-container {
#apply py-2;
#apply px-4;
}
.modal-footer-container {
#apply flex;
#apply border-t;
#apply border-gray-400;
#apply px-4;
#apply pt-2;
}
</style>
CertificateDetailsModal.vue:
<template>
<ModalTemplate ref="modal">
<template v-slot:modal-header>
This is a header
</template>
</ModalTemplate>
</template>
<script>
import ModalTemplate from '~/components/Modals/ModalTemplate'
export default {
name: 'DetailsModal',
components: {
ModalTemplate
},
model: {
prop: 'certificate',
event: 'input'
},
props: {
certificate: {
type: Object,
default: null
}
},
mounted() {
},
methods: {
closeModal: function() {
alert('closing modal!')
this.$store.dispatch('certificates/loadCertificates')
this.$emit('input', null);
}
}
}
</script>
<style scoped>
</style>
Attempts:
I looked at extending the modal, but it gave me quite a few errors when I tried to dismiss the modal (I can provide the code if needed).
Question:
How can I extend the Modal (getting all the functionality of the functions) while adding additional functionality in the CertificateDetailsModal (such as functions, methods, and html)?
You could re-declare the slots in your wrapper component's template. For instance, the following template declares a modal-footer slot and a default slot (unnamed assumed to have a name of default):
<!-- CertificateDetailsModal.vue -->
<template>
<ModalTemplate>
<template v-slot:modal-header>
My header
</template>
<template v-slot:modal-footer> <!-- pass `modal-footer` slot to ModalTemplate -->
<slot name="modal-footer"></slot>
</template>
<slot /> <!-- pass `default` slot to ModalTemplate -->
</ModalTemplate>
</template>
Then your app could use the CertificateDetailsModal like this:
<!-- App.vue -->
<template>
<CertificateDetailsModal>
<template v-slot:modal-footer>
<footer>My footer</footer>
</template>
<span>My default</span>
</CertificateDetailsModal>
</template>
demo
I'm using app-layout with app-drawer / app-drawer-layout and app-header / app-header-layout (see MWE below). The paper-badge is attached to an element inside the app-header. The first time the page is loaded, it briefly appears in the correct position (issue #12), than disappears off the right of the screen, exactly 256px too far to the right, which is the exact size of the app-drawer.
When the screen is resized, it recalculates the position to the correct place. I suspect that the position is calculated before the drawer is fully loaded.
This does not happen when the app-drawer is retracted and not visible.
I tried running updatePosition() at ready but that did not help.
Here is my MWE:
<template>
<style is="custom-style">
app-drawer {
--app-drawer-content-container: {
background-color: green;
};
}
app-header {
background-color: yellow;
}
</style>
<app-drawer-layout fullbleed>
<app-drawer slot="drawer">
<div id="drawerbox">Navigation Drawer</div>
</app-drawer>
<div class="container">
<app-header-layout>
<app-header id="header" condenses reveals effects="waterfall" slot="header">
<app-toolbar id="toolbarheader">
<div main-title id="toolbartitlebox">Title Toolbar</div>
<paper-icon-button id="discoursebutton" icon="communication:forum"></paper-icon-button>
<paper-badge for="discoursebutton" label="20" title="20 results"></paper-badge>
</app-toolbar>
</app-header>
</app-header-layout>
</div>
</app-drawer-layout>
</template>
One hack that works is to set a setTimeouton the updatePosition():
ready: function() {
var self = this;
setTimeout(function(){
self.$$('paper-badge').updatePosition();
}, 100);
}
But is there a more elegant way of solving this?
Thanks for your help!
One solution is to put paper-badge outside/after app-drawer-layout :
<template>
<style>
app-drawer {
--app-drawer-content-container: {
background-color: green;
}
;
}
app-header {
background-color: yellow;
}
paper-badge {
margin-top: 10px;
}
</style>
<app-drawer-layout fullbleed>
<app-drawer id="drawer" slot="drawer">
<div id="drawerbox">Navigation Drawer</div>
</app-drawer>
<app-header-layout>
<app-header id="header" condenses reveals effects="waterfall" slot="header">
<app-toolbar id="toolbarheader">
<paper-icon-button id="toggle" on-tap="_onToggle" icon="menu"></paper-icon-button>
<div main-title id="toolbartitlebox">Title Toolbar</div>
<paper-icon-button id="discoursebutton" icon="inbox"></paper-icon-button>
<!-- <paper-badge for="discoursebutton" label="20" title="20 results"></paper-badge>-->
</app-toolbar>
</app-header>
</app-header-layout>
<div class="container">
container
</div>
</app-drawer-layout>
<paper-badge for="discoursebutton" id="pd" label="20" title="20 results"></paper-badge>
</template>
Plnkr: http://plnkr.co/edit/V7zGBpqqzRonbAYyudea?p=preview
I avoided this same issue by putting the <paper-icon-button> and <paper-badge> in a container with position: relative, and stopped using the for attribute.
<div style="position: relative">
<paper-icon-button id="notification-icon" icon="social:notifications" onclick="[[showNotifications]]">
</paper-icon-button>
<template is="dom-if" if="[[messages.length]]">
<paper-badge label="[[messages.length]]" class="red"></paper-badge>
</template>
</div>