I'm using Hammerjs 2.0.8 with Nuxt, and I can't seem to make it detect swipe motion. Here's how I've done the setup. If I change the recognizer to [Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }] and on pan event hammer.on('pan', this.handleTouchEvents); it runs this.handleTouchEvents method.
<template>
<v-app>
<v-main>
<v-container id="touch-container" fluid>
<div class="items">
...
</div>
</v-container>
</v-main>
</v-app>
</template>
<script>
export default {
mounted () {
const Hammer = require('hammerjs')
const touchContainer = document.getElementById('touch-container')
const hammer = new Hammer.Manager(touchContainer, {
recognizers: [
[Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
})
hammer.on('swipe', this.handleTouchEvents)
},
methods: {
handleTouchEvents (e) {
alert(e.type)
}
}
}
</script>
<style scoped>
#touch-container {
height: 100%;
min-width: 100%;
overflow-x: hidden;
}
#items {
touch-action: pan-y;
}
</style>
Any idea how to resolve this issue?
touchContainer is probably undefined.
2 Things:
Check if its on client side with process.client
Wait until Vue created the elements on the UI with this.$nextTick()
Here try this:
async mounted () {
if(process.client) {
await this.$nextTick();
const Hammer = require('hammerjs')
const touchContainer = document.getElementById('touch-container')
const hammer = new Hammer.Manager(touchContainer, {
recognizers: [
[Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
})
hammer.on('swipe', this.handleTouchEvents)
}
},
Related
I am using the vue-codemirror package to display css code on a website. The problem is that I don't understand how to update the state I get with vuex
<div>
<codemirror v-model="code" />
<button #click="change">click</button>
{{ $store.state.background }}
</div>
methods: {
change() {
Vue.set(this.$store.state, "background", "#242424");
},
},
data() {
return {
code: dedent`
/* Some example CSS */
body {
margin: ${this.$store.state.background};
padding: 3em 6em;
}
`,
};
},
Vuex
export default new Vuex.Store({
state: {
background: "#000",
},
});
I thought that the problem was reactivity and decided to use Vue.set when clicking on the click button, the value of {{ $store.state.background }} changes, but the code that is inside the data return does not change
You can also see this example in codesandbox
Like #vanblart commented You can create computed property, instead data:
computed: {
code() { return dedent(`
/* Some example CSS */
body {
margin: ${this.$store.state.background};
padding: 3em 6em;
}
`)
}
and in Vuex you can create action/mutation for setting values:
mutations: {
addBkg(state, val) {
state.background = val
},
},
actions: {
setBkg({ commit }, data) {
commit("addBkg", data);
},
},
and in methos dispatch that action:
change() {
this.$store.dispatch("setBkg", "#242424");
},
I want to be able to add a component to my template by clicking on a button. Let me show you what I have:
Draggable.vue is the component that I would like to add when I click on a button.
<template>
<div ref="draggableContainer" id="draggable-container" #mousedown="dragMouseDown">
<div id="draggable-header" >
<slot name="header"></slot>
</div>
<slot name="main"></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
name: 'DraggableDiv',
data: function () {
return {
positions: {
clientX: undefined,
clientY: undefined,
movementX: 0,
movementY: 0
}
}
},
methods: {
dragMouseDown: function (event) {
event.preventDefault()
// get the mouse cursor position at startup:
this.positions.clientX = event.clientX
this.positions.clientY = event.clientY
document.onmousemove = this.elementDrag
document.onmouseup = this.closeDragElement
},
elementDrag: function (event) {
event.preventDefault()
this.positions.movementX = this.positions.clientX - event.clientX
this.positions.movementY = this.positions.clientY - event.clientY
this.positions.clientX = event.clientX
this.positions.clientY = event.clientY
// set the element's new position:
this.$refs.draggableContainer.style.top = (this.$refs.draggableContainer.offsetTop - this.positions.movementY) + 'px'
this.$refs.draggableContainer.style.left = (this.$refs.draggableContainer.offsetLeft - this.positions.movementX) + 'px'
},
closeDragElement () {
document.onmouseup = null
document.onmousemove = null
}
}
}
</script>
<style>
#draggable-container {
position: absolute;
z-index: 9;
height: 240px;
width: 152px;
background-color: rgb(170, 104, 43);
}
#draggable-header {
z-index: 10;
}
</style>
And here is my App.vue:
<template>
<div id = 'dd'>
<button #click="createDraggableDiv">Créer bloc</button>
<DraggableDiv class="col-11">
<template>
</template>
</DraggableDiv>
</div>
</template>
<script>
import DraggableDiv from './components/DraggableDiv.vue'
export default {
name: 'App',
components: {
DraggableDiv,
},
methods:{
createDraggableDiv(){
//function to add the DraggableDiv component to my template
}
}
}
</script>
<style>
</style>
I already have a DraggableDiv on my app when I launch it but I want to be able to add some more with the button.
I you have any idea to help me do that, please tell me!
Thanks!
You can achieve this using the v-for directive:
<template>
<div>
<your-component v-for="index in count" :key="index"></p>
<button v-on:click="addComponent">Add</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 1
};
},
methods: {
addComponent() {
this.count += 1;
}
}
};
</script>
Hi guys and thanks in advance for your time on this.
I'm very new to Vue-js and I was wondering if you could help me understand where I'm being bone-headed.
I am trying to use a toggle-button that will activate-deactivate a certain component.
I have done the database implementation stuff i.e. the change is reflected on the database side and other places just fine.
I clearly don't properly understand either the lifecycle or something else but my 'new' toggle value is not being picked up when read from the database: meaning I switch it from 'on' to 'off' and I see it in the database. When I come back to it the toggle is showing 'on'.
I added the code snippet:
<template>
<div class="asset">
<div class="loading" v-if="loading"></div>
<div class="content" v-else>
<b-row>
<b-col cols="12" style="text-align: right;">
<toggle-button :width="70"
:labels="{checked: 'Active', unchecked: 'Inactive'}"
:value="activeStatus"
#change="handleStatusChange"/>
</b-col>
</b-row>
<h2><span>Asset</span> {{ asset.name }}</h2>
...
</div>
</div>
</template>
<script>
import { ToggleButton } from 'vue-js-toggle-button';
export default {
name: "Asset",
components: {
ToggleButton
},
data() {
return {
assetId: 0,
asset: null,
activeStatus: true,
};
},
methods: {
getActiveStatus() {
this.$http.get(`asset/${this.assetId}/status`)
.then((resp) => {
this.activeStatus = resp.bodyText;
<!-- logging for testing only-->
this.$httpError("Retrieved ActiveStatus: " + this.activeStatus);
})
.catch((resp) => {
this.$httpError("Cannot retrieve active status");
});
},
handleStatusChange(event) {
let newStatus = { activeStatus: event.value };
this.$http.post(`asset/${this.assetId}/status`, newStatus).then(() => {
this.activeStatus = newStatus;
}).catch((resp) => {
this.$httpError('Failed to update activeStatus', resp);
});
},
loadAsset() {
this.loading = true;
this.$http
.get(`asset/${this.assetId}`)
.then((resp) => {
this.asset = resp.body;
})
.catch((resp) => {
this.$httpError("Failed to load asset", resp);
})
.finally(() => {
this.loading = false;
});
},
},
created() {
this.assetId = this.$route.params.id;
this.getActiveStatus();
this.loadAsset();
},
};
</script>
<style scoped>
h2 span {
font-size: 12px;
display: block;
text-transform: uppercase;
}
#dbButton,
#dupButton {
width: 30%;
}
#redspan {
color: red;
}
#copyButton,
#pasteButton {
width: 10%;
}
</style>
Just add one line ":sync=true" to the toggle-button component as an attribute.
In your case, the code looks like this:
<toggle-button :width="70"
:labels="{checked: 'Active', unchecked: 'Inactive'}"
:value="activeStatus"
:sync=true
#change="handleStatusChange"/>
Thanks
I want to apply mini-variant to the v-navigation-drawer when the screen size is smaller so for that I have the following so far:
<template >
<v-app id="inpire">
<div class="back">
<v-app-bar app>
<v-app-bar-nav-icon #click="drawer = !drawer"></v-app-bar-nav-icon>
<v-spacer></v-spacer>
<h1>{{selectedMethod}}</h1>
</v-app-bar>
<v-navigation-drawer v-model="drawer" src="./assets/bg.png" green app :mini-variant="mini">
<v-list-item
class="y"
v-for="item in items"
:key="item.unidade"
:to="item.link"
#click="change(item.unidade)"
>{{item.unidade}}</v-list-item>
</v-navigation-drawer>
<v-content id="inspire">
<router-view :dcsi="dcsi" :ipe="ipe" :rt="rt" :key="compKey"></router-view>
</v-content>
</div>
</v-app>
</template>
<script>
export default {
name: "App",
data: () => ({
items: [
{ unidade: "IPE", link: "/ipe" },
{ unidade: "DCSI", link: "/dcsi" },
{ unidade: "RT", link: "/rt" }
],
drawer: false,
selectedMethod: "",
unidade: "",
compKey: 0,
methods: {
change(val) {
this.selectedMethod = val;
this.cKey();
},
cKey() {
this.compKey += 1;
console.log(this.compKey);
}
},
watch: {
"$route.params.uni": function() {
this.cKey();
console.log(this.$route.params.uni);
console.log(this.props);
}
},
computed: {
mini() {
switch (this.$vuetify.breakpoint.name) {
case "xs":
return true;
case "sm":
return true;
case "md":
return true;
case "lg":
return false;
case "xl":
return false;
}
}
}
};
</script>
<style lang="stylus" scoped>
#inspire {
background: require('./assets/mg1.jpg') no-repeat center center;
}
.y {
color: green;
}
</style>
As a computed property I wrote mini() where it returns true or false depending on screen size.
But I am getting the following error "162:9 error Expected to return a value in "mini" computed property".
I don't understand why since it is returning a boolean.
I also tryed adding as a prop "mini-variant-md-and-down" to the navigation-drawer which didn't return any error but also didn't work.
Any hints in making the v-navigation-drawer become mini on smartphone are welcome.
mounted() {
if (
this.$vuetify.breakpoint.name === "md" ||
this.$vuetify.breakpoint.name === "sm" ||
this.$vuetify.breakpoint.name === "xs"
) {
this.mini = true;
}
Solved it
I'm working with pure JS hooks for the <transition-group> element on VueJS, and I am quite puzzled on how the enter hook actually works. Based on the documentation, I understand that I will have to call done() to avoid events being called synchronously:
When using JavaScript-only transitions, the done callbacks are required for the enter and leave hooks. Otherwise, the hooks will be called synchronously and the transition will finish immediately.
However, even when I use it, it seems to stop CSS transitions from happening in the entering transition. The only solution I found is to use window.setTimeout to set the style, which I think is a code smell. Here is a quick visual comparison between the code without the timeout, and the one with (the one with the timeout is the desired effect):
Broken enter transition (no transition of the left padding and opacity):
Desired enter transition:
In the example below, I am displaying a list using <transition-group> and wanted to use JS-hooks so that I can create staggered paddings on individual list items. It seems to work with the exception that in the enter transition, the CSS transitions on the padding property does not work.
new Vue({
el: '#app',
data: {
items: [
'Lorem',
'Ipsum',
'Dolor',
'Sit',
'Amet'
],
toggle: false
},
computed: {
filteredItems: function() {
if (!this.toggle)
return [];
return this.items;
}
},
methods: {
toggleItems: function() {
this.toggle = !this.toggle;
},
beforeEnter: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
},
enter: function(el, done) {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
done();
},
beforeLeave: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
}
}
})
ul {
list-style: none;
margin: 0;
padding: 0;
}
ul li {
transition: all 500ms ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggleItems">
Toggle items
</button>
<transition-group
tag="ul"
#before-enter="beforeEnter"
#enter="enter"
#before-leave="beforeLeave">
<li
v-for="(item, i) in filteredItems"
v-bind:key="i"
v-bind:data-index="i">
{{ item }}
</li>
</transition-group>
</div>
If you wrap all the logic inside the enter method inside an arbitrary timout, then it works:
enter: function(el, done) {
window.setTimeout(() => {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
done();
}, 100);
},
And this is where I am a little confused: does the enter hook not wait for beforeEnter to complete first? The working snippet is as follow
Changing the #enter hook to #after-enter should fix it
I have no clue why the #enter hook isn't working for this, as looking at the documentation it should but this should at least get rid of the timeout without being a hack
new Vue({
el: '#app',
data: {
items: [
'Lorem',
'Ipsum',
'Dolor',
'Sit',
'Amet'
],
toggle: false
},
computed: {
filteredItems: function() {
if (!this.toggle)
return [];
return this.items;
}
},
methods: {
toggleItems: function() {
this.toggle = !this.toggle;
},
beforeEnter: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
},
afterEnter: function(el) {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
},
beforeLeave: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
}
}
})
ul {
list-style: none;
margin: 0;
padding: 0;
}
ul li {
transition: all 500ms ease-in-out;
}
li.v-enter-active {
transition: none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="toggleItems">
Toggle items
</button>
<transition-group
tag="ul"
#before-enter="beforeEnter"
#after-enter="afterEnter"
#before-leave="beforeLeave">
<li v-for="(item, i) in filteredItems" v-bind:key="i" v-bind:data-index="i">
{{ item }}
</li>
</transition-group>
</div>
As a side note, if you're using SCSS or SASS, you can achieve this with that rather than JavaScript