Aurelia event bubbling issue (?), behavior different since 1.0 - aurelia

Reproducible via au new (Aurelia CLI), code below.
It worked perfectly before the update to 1.0.
The problem: When hitting the "Close" button, closePopup() is called but immediately afterwards openPopup() as well. The result is, that the popup does not close. Even click.trigger which shouldn't bubble up the event does not solve the problem.
How to solve this? Why has that behavior changed?
app.html
<template>
<div click.delegate="openPopup()" style="border: 1px solid black; width: 100px; height: 100px">
<div show.bind="_expanded">
Foo <button click.trigger="closePopup()">Close</button>
</div>
</div>
</template>
app.ts
export class App {
_expanded;
openPopup() {
this._expanded = true;
console.log("Opened");
}
closePopup() {
this._expanded = false;
console.log("Closed");
}
}

Solution (event.stopPropagation()):
app.html
<template>
<div click.delegate="openPopup()" style="border: 1px solid black; width: 100px; height: 100px">
<div show.bind="_expanded">
Foo <button click.trigger="closePopup($event)">Close</button>
</div>
</div>
</template>
app.ts
// ...
closePopup(event) {
event.stopPropagation();
this._expanded = false;
console.log("Closed");
}
// ...

Related

Vue Video Background does not start

I'm starting as a frontend developer with VueJs and I'm having a problem that I can't understand... So I hope if some could help me... I'm currently using avidofood
/
vue-responsive-video-background-player
Because I need some kind of video playlist in the background. I implemented such as official documentation said (at least I think so..) but nothing happened, I mean, I have no errors, but the video does not start. This is my code:
In App.vue
<template>
<div id="App">
<video-background
src="https://www.youtube.com/watch?v=PpAxlizirDI"
style="max-height: 80%; height: 100vh"
>
<h1 style="color: white">Test App!</h1>
</video-background>
<div class="container mt-5">
<div class="row">
<div class="col-xl-12 col-lg-10">
<div class="form-group barcode">
<div class="row">
<div class="col">
<input
id="barcode"
type="text"
name="barcode"
class="form-control pl-3 shadow-none bg-transparent border-0"
placeholder="Acerca el producto al lector..."
autofocus
onfocus="this.value=''"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
//Oculto el mouse
//document.body.style.cursor = "none";
export default {
name: "App",
};
</script>
<style>
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
}
body {
background: #eee;
}
.text-color {
color: rgb(212, 14, 14) !important;
}
.barcode {
border: 1px solid;
border-color: #cccccc;
border-radius: 45px;
}
.container {
position: absolute;
bottom: 0px;
left: 10%;
margin: 0 auto;
}
</style>
In main.js
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm';
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue/dist/bootstrap-vue.css';
import VideoBackground from "vue-responsive-video-background-player";
Vue.component("video-background", VideoBackground);
Vue.use(BootstrapVue);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#App')
In Vue dev Tool:
In Chrome Console:
This is de Project Structure:
I hope someone could help me because I have no little idea what's going on.
Thanks in advance!
As source, you have a YouTube page, this is not a video.
<video-background
src="https://www.youtube.com/watch?v=PpAxlizirDI"
style="max-height: 80%; height: 100vh"
>
This is your path to your video. You can just use this value for
showing your video in every resolution.
The source of the video has to be in a format the browser can play (mp4 is a good format to use).
For example:
<video-background
src="/files/my-video.mp4"
style="max-height: 80%; height: 100vh">
Update based on your comment:
Because vue gets compiled it does not know what assets/demo.mp4 is,. You should import it first via an loader.
<script>
import video from 'assets/demo.mp4' // Or '#/assets/demo.mp4' depending on what loader you use.
export default {
name: "App",
};
</script>
And now you could use:
<video-background
:src=video
style="max-height: 80%; height: 100vh">

How do you apply Vue Transitions when Changing CSS Class?

I can't get Vue to apply Transition animation effects when changing from one CSS class to another (using V-bind).
I've gone through the Vue documentation but none of the examples work for my use case. I basically want the DIV to transition by fading nicely from full screen to "regular size" by toggling.
Please see Fiddle at https://jsfiddle.net/luckman8/892ktedz/
`
<div v-bind:class="{'fullscreen':isFullScreen, 'regularSize':!isFullScreen}">
<button type="button" #click="toggle">
Toggle
</button>
<p>
{{isFullScreen}}
</p>
</div>
`
The CSS:
.fade-enter-active, .fade-leave-active {
transition: opacity 3s ease-out;
}
.fade-enter, .fade-leave-to {
opacity: 0.1;
}
.regularSize
{
width: "50%";
height: "50%";
background-color: "green";
}
.fullscreen
{
display: flex;
flex-direction: column;
position: fixed;
background-color:blue;
top:0;
left:0;
width: 100%;
height: 100%;
flex-basis:10%;
overflow-y:hidden;
}
The Vue code:
new Vue({
el: "#app",
data: {
isFullScreen:false
},
methods: {
toggle()
{
this.isFullScreen = !this.isFullScreen
}
}
})
Thank you in advance!
The <transition> effects will only work for elements that change their visibility. So for example by using v-if.
So you have two options.
Either add a v-if logic to your template.
For example: (a quick mock up. could be refactored to be cleaner)
<div id="app">
<button type="button" #click="toggle">
Toggle
</button>
<transition name="fade">
<div
v-if="isFullScreen"
v-bind:class="{
'fullscreen':isFullScreen,
'regularSize':!isFullScreen
}">
<p>FULL SCREEN</p>
<button type="button" #click="toggle">
Toggle
</button>
</div>
</transition>
<transition name="fade">
<div
v-if="!isFullScreen"
v-bind:class="{
'fullscreen':isFullScreen,
'regularSize':!isFullScreen
}">
<p>SMALL SCREEN</p>
</div>
</transition>
</div>
Create the transition effect purely with css.

Why does my v-if not fire when using modals with Vue?

I'm using v-if to control the display of pages in my Vue app. the 'page' data property keeps track of the current page and is updated by button clicks. This works beautifully until I introduce modals, as now when I open a modal and navigate back a couple of pages (using my app's navigation buttons) the page fails to display despite the property being updated correctly.
Here's a simplified example - navigate to page B then C then display Modal 2. Cancel Modal 2, then navigate to Page B and nothing displays (despite the header indicating that the page property is B).
https://jsfiddle.net/fLmq0dxn/1/
I've tried this approach with both bootstrap modals and native js modals but the same problem occurs. No errors reported in the console. I thought it might be wrongly nested divs but I've checked these and put it through a validator.
I realise that my navigation methods are primitive and that the modals probably should be components, but I'm a newbie to Vue, and as far as I understand it my approach 'should' work. Can anyone explain why it doesn't please?
HTML:
<div id="app">
<p>(app.page = {{page}})</p>
<br/>
<div class="page" id="A" v-if="page=='A'">
Page A
<br/>
<button v-on:click="pager('B')">To B</button>
</div>
<div class="page" id="B" v-if="page=='B'">
Page B
<br/>
<button v-on:click="pager('C')">To C</button>
<button v-on:click="modalOpen('mod1')">Modal</button>
</div>
<!-- ************ Modal 1 ************************************ -->
<div id="mod1" class="mod">
<div class="mod-content">
<span class="mod-close" v-on:click="modalClose">×</span>
<h1>Modal 1</h1>
<button v-on:click="modalClose" class="btn btn-secondary">Cancel</button>
</div>
</div>
<div class="page" id="C" v-if="page=='C'">
Page C
<br/>
<button v-on:click="pager('B')">To B</button>
<button v-on:click="modalOpen('mod2')">Modal</button>
</div>
<!-- ************ Modal 2 ************************************ -->
<div id="mod2" class="mod">
<div class="mod-content">
<span class="mod-close" v-on:click="modalClose">×</span>
<h1>Modal 2</h1>
<button v-on:click="modalClose" class="btn btn-secondary">Cancel</button>
</div>
</div>
</div>
CSS:
/* The Modal (background) */
.mod {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content/Box */
.mod-content {
background-color: #fefefe;
margin: 20% auto;
padding: 20px;
border: 1px solid #888;
border-radius:8px;
width: 90%;
max-width:800px;
}
/* The Close Button */
.mod-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.mod-close:hover,
.mod-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
Javascript:
new Vue({
el: "#app",
data: {
page: "A"
},
methods: {
pager: function(target){
this.page=target;
},
modalOpen: function(modID) {
$('#'+ modID).css('display','block');
},
modalClose: function(){
$('.mod').css('display','none');
}
}
})
Combining Vue with jQuery is risky unfortunately.
In your specific case, it seems like when you try closing your modal, jQuery looks for all elements with "mod" class, but when hiding them, the selection is tampered by Vue and you end up with incorrect elements being hidden (in your case, the content of your page B). Vue is not designed to have another library fiddling with the DOM.
You can "easily" achieve your goal using Vue only. Since you manage your modal by changing their style, you can do something similar with Vue class and/or style binding.
E.g. you could have a class that overrides your display: none, and you conditionally apply that class based on a data, very similarly as you do for your pages. And you could even probably manage your modal with v-if, exactly like you did with your pages.
Example with conditional class: https://jsfiddle.net/jfx8mbya/
Example with modal managed by v-if:
new Vue({
el: "#app",
data: {
page: "A",
modal: null
},
methods: {
pager: function(target) {
this.page = target;
},
modalOpen: function(modID) {
this.modal = modID;
},
modalClose: function() {
this.modal = null;
}
}
})
/* The Modal (background) */
.mod {
/*display: none;*/
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
}
/* Modal Content/Box */
.mod-content {
background-color: #fefefe;
margin: 20% auto;
padding: 20px;
border: 1px solid #888;
border-radius: 8px;
width: 90%;
max-width: 800px;
}
/* The Close Button */
.mod-close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.mod-close:hover,
.mod-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p>(app.page = {{page}})</p>
<br/>
<div class="page" id="A" v-if="page=='A'">
Page A
<br/>
<button v-on:click="pager('B')">To B</button>
</div>
<div class="page" id="B" v-if="page=='B'">
Page B
<br/>
<button v-on:click="pager('C')">To C</button>
<button v-on:click="modalOpen('mod1')">Modal</button>
</div>
<!-- ************ Modal 1 ************************************ -->
<div id="mod1" class="mod" v-if="modal === 'mod1'">
<div class="mod-content">
<span class="mod-close" v-on:click="modalClose">×</span>
<h1>Modal 1</h1>
<button v-on:click="modalClose" class="btn btn-secondary">Cancel</button>
</div>
</div>
<div class="page" id="C" v-if="page=='C'">
Page C
<br/>
<button v-on:click="pager('B')">To B</button>
<button v-on:click="modalOpen('mod2')">Modal</button>
</div>
<!-- ************ Modal 2 ************************************ -->
<div id="mod2" class="mod" v-if="modal === 'mod2'">
<div class="mod-content">
<span class="mod-close" v-on:click="modalClose">×</span>
<h1>Modal 2</h1>
<button v-on:click="modalClose" class="btn btn-secondary">Cancel</button>
</div>
</div>
</div>

Polymer Page with different app-header on home page

i'm trying to make difference between home page and other page in polymer 2.0
Home page = With big header and condensed
Other Page = just as usual app-header with small header on the top
what i try to do is using dom-if, i create dom-if with 2 kind of app-header
_isHome(page) {
return page === "view1";
}
app-header#homeHeader {
color: #fff;
height: 500px;
--app-header-background-front-layer: {
background-image: url(../images/Digital-Signage-Slider3a.jpg);
background-size: cover;
background-repeat: no-repeat;
}
;
}
app-header#defaultHeader {
color: #fff;
background-color: var(--app-primary-color);
}
<app-header-layout>
<template is="dom-if" if="[[_isHome(page)]]">
<app-header id="homeHeader" slot="header" condenses reveals effects="parallax-background" style="height: 500px">
<app-toolbar>
<div main-title>My App</div>
<paper-icon-button icon="my-icons:menu" drawer-toggle></paper-icon-button>
</app-toolbar>
</app-header>
</template>
<template is="dom-if" if="[[!_isHome(page)]]">
<app-header id="defaultHeader" slot="header" condenses reveals effects="material">
<app-toolbar>
<div main-title>My App</div>
<paper-icon-button icon="my-icons:menu" drawer-toggle></paper-icon-button>
</app-toolbar>
</app-header>
</template>
<iron-pages selected="[[page]]" attr-for-selected="name" fallback-selection="view404" role="main">
<my-view1 name="view1"></my-view1>
<my-view2 name="view2"></my-view2>
<my-view3 name="view3"></my-view3>
<my-view404 name="view404"></my-view404>
</iron-pages>
</app-header-layout>
and guess what? it worked, but with some space after it, but the space is gone after refreshed hmm..
Home Page with large image condensed header
After click on the 2nd page
After refreshed
any idea?
Revised Solution
I had another go at this because on an app I started yesterday. I think I've a simplier solution, with only one app-header and one app-toolbar.
First I replace the app-header#... styles with these:
.main-title {
color: #fff;
height: 500px;
--app-header-background-front-layer: {
background-image: url(../images/Digital-Signage-Slider3a.jpg);
background-size: cover;
background-repeat: no-repeat;
}
;
}
.condensed-title {
color: #fff;
background-color: var(--app-primary-color);
}
The header and toolbar are now
<app-header-layout id="header" has-scrolling-region>
<app-header class$=[[_getHeaderClass(page)]] slot="header" fixed effects="waterfall">
<app-toolbar>
<template is="dom-if" if=[[_isHome(page)]]>
<div main-title>My App 1</div>
</template>
<template is="dom-if" if=[[!_isHome(page)]]>
<div condensed-title>My App 2 & 3</div>
</template>
<paper-icon-button icon="my-icons:menu" drawer-toggle></paper-icon-button>
</app-toolbar>
</app-header>
...
The function called to set app-header's class is
_getHeaderClass(page) {
return (page === "view1") ? 'main-title' : 'condensed-title';
}
Note that you must still call this.$.header.notifyResize() in _routePageChanged(page).
I've put up a gist of the complete file at my-app-revised.html.
----- former solution -----
Call notifyResize() on the app-header-layout.
If you look at the polymer-starter-kit, give the app-header-layout an id, like id="header":
<app-header-layout id="header" has-scrolling-region>
Then call this.$.header.notifyResize() in _routePageChanged(page):
_routePageChanged(page) {
// If no page was found in the route data, page will be an empty stri.
// Default to 'view1' in that case.
this.page = page || 'view1';
this.$.header.notifyResize();
// Close a non-persistent drawer when the page & route are changed.
if (!this.$.drawer.persistent) {
this.$.drawer.close();
}
}

Transform Based Animation using Vue Transition Wrapper

I'm trying to port some animation implemented mostly through css and little javascript to a Vue component. The animation is simple - user clicks a button and a little panel opens from the bottom of his browser and slides upwards.
I have a working Vue component implemented using the same css and no javascript.
Now, I'm aware of the transition wrapper that Vue provides. But I'm unable to figure out how to get similar functionality using the transition wrapper (if at all).
Can someone help me out here?
// register modal component
Vue.component('modal', {
template: '#modal-template',
props: ['show']
})
// start app
new Vue({
el: '#app',
data: {
showModal: false
}
})
.drawer-wrapper {
position: fixed;
top: 100%;
left: 0;
right: 0;
height: 50%;
z-index: 9998;
transition: transform .3s ease-out;
}
.drawer-wrapper.open {
transform: translateY(-100%);
}
.drawer-container {
height: 100%;
width: 100%;
background-color: white;
}
.drawer-header h3 {
margin-top: 0;
color: #42b983;
}
.drawer-body {
margin: 20px 0;
}
.drawer-default-button {
float: right;
}
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<!-- template for the modal component -->
<script type="text/x-template" id="modal-template">
<div class="drawer-wrapper" :class="{ open: show }">
<div class="drawer-container">
<div class="drawer-header">
<slot name="header">
default header
</slot>
</div>
<div class="drawer-body">
<slot name="body">
default body
</slot>
</div>
<div class="drawer-footer">
<slot name="footer">
default footer
<button class="drawer-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</script>
<!-- app -->
<div id="app">
<button id="show-modal" #click="showModal = true">Show Modal</button>
<!-- use the modal component, pass in the prop -->
<modal #close="showModal = false" :show="showModal">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
</div>
I got this to work using a transition wrapper, but the code isn't as elegant as I'd expected. Particularly the use of the following attributes in the drawer-wrapper class:
top: 100%;
transform: translateY(-100%);
Fiddle is here
If anyone can simplify the code further, I'd really appreciate it.