I am building a demo app, used for handling data. There is a lot of actions available. So I decide to implement a quick menu, which navigate the user to the detail panel, using the right click menu.
I try to use vue-router to mark all function with a unique path, like /action/info, /action/merge. But it turns out to be lack of management.
Are there any good way to solve it?
Vue-router makes sense to use if your actions demand a full screen. Otherwise, you can use a simple dialogue. But if you don't want to consider each action as a separate page, I see two options:
Option 1: You can use full-screen dialogues: you can show them without changing the route.
Option 2: You can use alternative navigation component that navigates without routes. It's not necessary to use it for the whole app. You can use it only for the page where you call all your actions. For example, take a look at v-ons-navigator from Onsen UI. It doesn't use routes, but a stack:
// Go to the "main" page by Vue-router and use v-ons-navigator inside to navigate
// between actions.
<template id="main">
<v-ons-navigator swipeable
:page-stack="pageStack"
#push-page="pageStack.push($event)"
></v-ons-navigator>
</template>
<template id="page">
<v-ons-page>
<v-ons-toolbar>
<div class="center">Page</div>
</v-ons-toolbar>
<p style="text-align: center">
This is the page
<v-ons-button #click="push">Action!</v-ons-button>
</p>
</v-ons-page>
</template>
<template id="action">
<v-ons-page>
<v-ons-toolbar>
<div class="left">
<v-ons-back-button>Page</v-ons-back-button>
</div>
<div class="center">Action!</div>
</v-ons-toolbar>
<p style="text-align: center">This is the page of the action</p>
</v-ons-page>
</template>
const action = {
key: 'action',
template: '#action'
};
const page = {
key: 'page',
template: '#page',
methods: {
push() {
// Here you go to the page of your action.
// You just push it in the stack without changing the route.
this.$emit('push-page', action);
}
}
};
const main = {
template: '#main',
data() {
return {
pageStack: [page]
}
}
};
Related
I am using a third party app (vue slick carousel) and I need to bind a click event to below button
<button type="button" data-role="none" class="slick-arrow slick-next" style="display: block;">
<svg...</svg>
</button>
So everytime I click this button a function gets triggered. This is what I tried in mounted and created life cycle, but didn't bind click event to it.
// console.log(document.querySelector(".slick-next")); // this returns element stated above
document.querySelector(".slick-next").addEventListener("click", () => {
console.log("Works");
});
I tried using setAttribute hoped it works, but it didn't work either.
Any help is appreciated. Thank you in advance.
I suspect they're probably using stopPropagation on the button click inside the component then.
There might be another way around this depending on your needs:
<VueSlickCarousel #beforeChange="checkChange">
...
</VueSlickCarousel>
methods: {
checkChange(oldSlideIndex, newSlideIndex) {
if (newSlideIndex > oldSlideIndex) {
console.log("Next button clicked");
}
}
}
Another option might be to use the <template #nextArrow="{ currentSlide, slideCount }"></template> slot inside the Carousel tag and use your own button. You'd probably have to implement your own logic for setting the next slide if you went this route.
You must to use the events handling of vuejs. Vuejs have many events for multiple cases.
For example in your case, you can use the event v-bind:click or simply #click. In documentation of vue slick, show this examples, wich is essentially the same.
I attach an code snippet:
<template>
<VueSlickCarousel ref="carousel">
<div><h3>1</h3></div>
/*...*/
</VueSlickCarousel>
<button #click="showNext">show me the next</button>
</template>
<script>
export default {
methods: {
showNext() {
this.$refs.carousel.next()
},
},
}
</script>
I have read this post which goes in depth about renderless components:
https://adamwathan.me/renderless-components-in-vuejs/
A renderless component would pretty much look like this:
export default {
render() {
return this.$scopedSlots.default({})
},
}
Now I would like to use this renderless component but also add a click listener to whatever is being passed into the slot.
In my case it would be a button. My renderless component would simply wrap a button and add a click listener to it, which in turn performs an AJAX request.
How would I go about adding a click listener to the element that is being passed into the slot?
Assuming you want to bind the click handler within the renderless component, I think from this post that you need to clone the vnode passed in to renderless, in order to enhance it's properties.
See createElements Arguments, the second arg is the object to enhance
A data object corresponding to the attributes you would use in a template. Optional.
console.clear()
Vue.component('renderless', {
render(createElement) {
var vNode = this.$scopedSlots.default()[0]
var children = vNode.children || vNode.text
const clone = createElement(
vNode.tag,
{
...vNode.data,
on: { click: () => alert('clicked') }
},
children
)
return clone
},
});
new Vue({}).$mount('#app');
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<renderless>
<button type="button" slot-scope="{props}">Click me</button>
</renderless>
</div>
Here's one way to go about this.
Your renderless component wrapper would consist of a single action (i.e. the function to issue the AJAX request) prop.
Vue.component('renderless-action-wrapper', {
props: ['action'],
render() {
return this.$scopedSlots.default({
action: this.action,
});
},
});
Then another component which uses the aforementioned wrapper would enclose a customisable slot with a #click handler, which invokes the action that is passed in when triggered.
Vue.component('clickable', {
props: ['action'],
template: `
<renderless-action-wrapper :action="action">
<span slot-scope="{ url, action }">
<span #click="action()">
<slot name="action"></slot>
</span>
</span>
</renderless-action-wrapper>
`,
});
Finally, wire up the specialised version of the wrapper.
<clickable :action="doAjaxRequest">
<button type="button" slot="action">Button</button>
</clickable>
Here's a live example of the above suggestion you can play around with.
My vue component like this :
<template>
<span class="rating">
...
</span>
</template>
<script>
export default {
props: {
'star': null
},
...
}
</script>
If the component is running I want to disable button back in the browser. So the user can not go back to the previous page
How can I do it?
Try this script, by adding in your html file, where you creat vue instance
history.pushState(null, null, location.href);
window.onpopstate = function () {
history.go(1);
};
Run this code whenever url changes. It will counteract user's back action
window.history.forward(1)
I have a logic like this: is the user is V2 use the user to the url in subHeaderRouter.router. If the user isn't launch this.openModal:
<router-link
v-for="subHeaderRouter in subHeaderRouters"
:to="subHeaderRouter.router"
#click="handleOpenModal()">
</router-link>
handleOpenModal () {
if (this.IsV2User) return
this.openModal('changeUserType', 'user.changeUserType')
}
The only thing I need to do now is to stop :to then she user is not V2. How to accomplish that?
You can prevent the default <router-link> behavior by specifying no default event to listen to and handling the click event manually with the .native modifier:
<router-link
v-for="subHeaderRouter in subHeaderRouters"
event=""
:to="subHeaderRouter.router"
#click.native.prevent="handleOpenModal(subHeaderRouter.router)"/>
handleOpenModal(route) {
if (this.IsV2User) {
this.$router.push(route)
} else {
this.openModal('changeUserType', 'user.changeUserType')
}
}
If the event="" seems weird to you, it also works with an empty attribute:
<router-link
v-for="subHeaderRouter in subHeaderRouters"
event
:to="subHeaderRouter.router"
#click.native.prevent="handleOpenModal(subHeaderRouter.router)"/>
Vue 3 Solution
In Vue3, the event has been removed from "<router-link>, you will need to use v-slot API instead.
<router-link :to="yourRoute" custom v-slot="{ href, navigate }">
<a v-if="yourBoolean" #click="handleEvent()">Run the function</a>
<a
v-else
:href="href"
#click="navigate">
Go to route in "to"
</a>
</router-link>
// Vue 3 Composition API
export default {
setup() {
const handleEvent = () => {
// Add your logic here
}
}
};
I had the same problem and used dynamic Vue Components as solution. You will also have to check the difference in styling for router and div tag.
<component
:is=" condition ? 'router-link' : 'div' "
#click="handleOpenModal"
:to="your_link">
</component>
I'm trying to create a meteor app where the login page shows nothing but my Welcome text and a sign in through Google. On my other pages I have a navbar. How do I exclude the navbar specifically from this login page? Does it have to do with iron:router? Is there some sort of special method that I call?
You can make 2 layouts like this.
<template name="layout">
<!-- Regular Stuff for the other pages You can place the navbar here -->
{{> yield}}
</template>
<template name="layoutLogin">
<!-- Just Login Pages -->
{{> yield}}
</template
Now the Javascript Code.
Router.map(function () {
this.route('home', {
path: '/',
layoutTemplate: 'layout'}
);
});
//Here we tell to render the template login, on the path /login and use the content on the layoutLogin
Router.map(function () {
this.route('login', {
path: '/login',
layoutTemplate: 'layoutLogin'}
);
});
Tell me if works.
You can create the template just for navbar and include it wherever required
<template name="top_navbar">
<!--your navbar code -->
</template>
<template name="mypage">
{{> top_navbar}}
<!-- rest of code for my page -->
</template>
<template name="mylogin">
<!-- rest of code for login page -->
</template>
I set up the two templates and then used the following which worked! The primary layout is the one with the navigation bar and the welcome layout is the one without it.
Router.configure({
layoutTemplate: 'primaryLayout'
});
Router.route('/', {layoutTemplate: 'welcomeLayout'});
Thank You!
There is much easier solution to your problem, just set layoutTemplate to null for your particular page that you want the nav and other layout to be excluded. In this case we will exclude the navigation for the login page:
Router.route('login', {
layoutTemplate: '' //the default template is set to null using ''
});