How to stop <router-link> from sending the user to another page? - vue.js

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>

Related

Nuxt : #click does not work with Nuxt-link

I am expecting to include on my web application an effect that underlines the section where we are in the list of sections.
I am working with Nuxt.
I don't know why the following code does not change the value of the boolean isActive.
<nuxt-link
:to="`${path}/${filterItem.filter}`"
:style='{"text-decoration": (isActive ? "underline" : "none")}'
#click="selectSeason(filterItem.filter) toggleUnderline()" >
methods: {
selectSeason(filter) {
this.$router.push(`${this.path}/${filter}`)
},
toggleUnderline() {
this.isActive = !this.isActive
}
},
You could probably achieve this with something like this
<template>
<nuxt-link to="/about" :custom="true">
<a #click="test">go to about page</a>
</nuxt-link>
</template>
<script>
export default {
methods: {
test() {
console.log('called test method')
},
},
}
</script>
As shown here: https://github.com/vuejs/router/issues/846#issuecomment-808150258
Even thought, this is probably not the best approach overall and seems quite too hacky for a simple use case.
Use a button tag if you want an action, otherwise put conditional logic on your route but don't mix a client side navigation and some kind of click events.
In Nuxt3 <NuxtLink> will get a class="router-link-active router-link-exact-active" if it's active, so you can use those to underline active link.
Same as router-link, you need to use v-on:click.native
<nuxt-link
:to="`${path}/${filterItem.filter}`"
:style='{"text-decoration": (isActive ? "underline" : "none")}'
#click.native="selectSeason(filterItem.filter) toggleUnderline()"
>
</nuxt-link>
How do we v-on:click nuxt-link?
Nuxt3:
<script lang="ts" setup>
const anyFunction = () => {
console.log('easy')
}
</script>
<template>
<NuxtLink
:to="'/'"
#click.prevent="anyFunction()"
>easy</NuxtLink>
</template>

how to redirect to specific component in vue js

I want to redirect inside a URL without page refresh, without using a router link as below :
<router-link to="/about us " active-class="active">foo</router-link>
I want to print routes like below:
<li class="nav-item phone">
<a class="nav-link" href="contact-us.html">
اتصل بنا
</a>
</li>
My route:
const routes = [
{ path: '/aboutus/', component: AboutUs }
]
Try this
this.$router.push('about')
You may need a workaround for this.
This solution won't change the url either :)
Set an html in the data
data: () => {
return {
html: null
}
}
Get the content of your html file using any request and assign to the html in data section. You can fetch this from any life cycle hook. Here I'm using beforeMount.
beforeMount() {
this.fetchAllEmployees();
axios.get('contact-us.html')
.then(response => {
this.html = response.data;
})
}
Now you can show the html content in your component like this
<template>
<div>
<div v-html="html"></div>
</div>
</template>
To show only when clicking the a tag, you can add another variable in the data which can be used to toggle the value.

How to implement the navigating action of right click menu in Vue?

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]
}
}
};

Disable nuxt link based on boolean

I can't seem to find a good way to disable a nuxt-link based on a data variable. Could anyone suggest something? I've tried looking at doucmentation but I can't come up with anything.
Say I have a boolean called disable I want to do something like this
<nuxt-link :disabled="disable"></nuxt-link>
I basically just don't want the link to be clickable if the boolean is set to false
<nuxt-link> is essentially <router-link> of Vue Router.
You can disable it using the event prop.
Assuming your one of your data or computed property is disabled boolean:
<nuxt-link :event="disabled ? '' : 'click'"></nuxt-link>
Working example:
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const Baz = { template: '<div>baz</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{ path: '/baz', component: Baz }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router,
data(){
return {
disabled: true
}
}
}).$mount('#app')
<script src="https://unpkg.com/vue#2.7.13/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-router#3.6.5/dist/vue-router.min.js"></script>
<div id="app">
<h1>Hello App!</h1>
<div>
<!-- Assume they are <nuxt-link> because they are the same -->
<router-link
:event="disabled ? '' : 'click'"
to="/foo"
>
Go to Foo ({{ disabled ? 'Disabled' : 'Enabled' }})
</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-link to="/baz">Go to Baz</router-link>
</div><br />
<button #click="disabled = !disabled">Toggle Foo availability</button><br /><br />
<router-view></router-view>
</div>
I found that the most simple way was to just create a class for the disabled links. I'm still not sure if this is the best way to do it but it works for me and does exactly what I want.
<nuxt-link to="/search" :class="{ disabled: disabled }"> Search </nuxt-link>
my css
.disabled {
color: lightgrey
pointer-events: none
}
Another way to do this, which I'd like the most. Is to change the tag to the button and use the native :disabled state.
<nuxt-link tag="button" :disabled="disabled" to="/some/location">Some location</nuxt-link>
Then just turn the button into the link with the help of styles.
In Nuxt3 this works for me: :to="canNavigate ? link : null"
In Vue Router 4, router-link do not support tag and event props anymore. Here you can have more information.
I'll copy the example from the link above here:
replace
<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
with
<router-link to="/about" custom v-slot="{ navigate }">
<span #click="navigate" #keypress.enter="navigate" role="link">About Us</span>
</router-link>
And I'll give a real example of usage. This piece of code I'm using in a real project, using nuxt and tailwind.
<nuxt-link
v-slot="{ navigate }"
class="flex-grow font-ember"
:to="`lesson/${l.lesson_id}`"
append
custom
>
<button
class="flex items-center"
:class="{
'text-gray-400 cursor-not-allowed': !l.released,
'text-gray-900': l.released,
}"
:disabled="!l.released"
#click="navigate"
>
...
</button>
</nuxt-link>
Just add the event prop if you simply want to disable a nuxt-link
<nuxt-link event="">Go to Foo</nuxt-link>
For this case anyway you can click on it. It displaying disabled but still clickable.
You need to use attribute tag in you nuxt-link
Example here <nuxt-link tag="button" :disabled="disable"></nuxt-link>
None of these seemed to work for me, so I set the :to on the nuxt-link to a computed method. The to attribute on nuxt link will redirect nowhere when empty. For example:
<script>
export default{
computed: {
redirectRequest(){
return this.isMobile ? "" : "/Worklog";
}
}
}
</script>
<template>
<NuxtLink :to="redirectRequest"></NuxtLink>
</template>

Renderless Vue component with a click listener

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.