How to use an image as a button in Vue.js? - javascript

I'm trying to make a login button as a single-file-component in Vue.js (it's a Rails app with a Vue.js front-end). If you click this button, it's supposed to take you to the an external provider's login page.
How can I use an image as a button? I'm guessing you use v-on:click for the actual redirect, but I'm stuck there.
Right now, this code below shows a hardcoded button that looks like img(src="../assets/img/login_button.png"). You can click on it, but that's obviously not what I want. I want to show the actual png image, not the path.
// LoginButton.vue
<template lang="pug">
#login-button
<button v-on:click="redirect_to_login">img(src="../assets/img/login_button.png")</button>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
#Component
export default class LoginButton extends Vue{
redirect_to_login():void{ // I haven't written this method yet
}
}
</script>

Is there any reason you can't just use normal HTML image inside your button? I haven't used pug before.
<button v-on:click="redirect_to_login"><img src="../assets/img/login_button.png" /></button
Though since you're using Vue and not an actual HTML form you might not even need a button you could just add the click binding to the image instead
<img src="../assets/img/login_button.png" v-on:click="redirect_to_login" />

I am not familiar with pug, so I don't know what the correct syntax you'll need is. But you can use the <router-link> tag to set the route. For example (using Vuetify)
<router-link to="/">
<v-img src="/path/to/img.gif"/>
</router-link>

Either you can use:
<a #click="Redirect">
<img src='IMAGE_SRC' />
</a>
or
<img #click="Redirect" src='IMAGE_SRC'/>
new Vue({
el: '#app',
methods:
{
Redirect()
{
window.location.href = "https://jsfiddle.net/";
//or
//this.$router.push('LINK_HERE'); // if ur using router
}
}
})
Demo LINK:
https://jsfiddle.net/snxohqa3/5/

Related

How to use a querySelector in Nuxt 3?

I'm trying to initialise IntersectionObserver in each page of my website built with Nuxt3.
Therefore, I want to access each HTML element that has a specific CSS class. However, on page change, I noticed that via onMounted hook the detected elements are from the previous page.
Here a easy to reproduce example:
app.vue
<template>
<div>
<NuxtPage />
</div>
</template>
pages/index.vue
<script setup lang="ts">
onMounted(() => {
console.group("index.vue");
console.log(document.querySelector("#container"));
console.groupEnd();
});
</script>
<template>
<div id="container">
<h1>INDEX</h1>
<NuxtLink to="/work">
Go to work
</NuxtLink>
</div>
</template>
pages/work.vue
<script setup lang="ts">
onMounted(() => {
console.group("work.vue");
console.log(document.querySelector("#container"));
console.groupEnd();
});
</script>
<template>
<div id="container">
<h1>WORK</h1>
<NuxtLink to="/">
Go to index
</NuxtLink>
</div>
</template>
Simply, the result in the console always come from the previous DOM. Here the steps:
Load the page on index.vue, you see the right element in the console.
Go to work.vue using the link.
See the console showing the exact same result as previously, yet with an added empty class attribute on #container
My question is, why does onMounted hook doesn't show the right DOM on page change?
I tried to set the page transition to the default mode:
definePageMeta({
pageTransition: {
mode: 'default',
},
});
Nothing changed.
NOTE: I am using nuxt version: 3.0.0-rc.9
For this kind of usage, you should use template refs: https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs
Otherwise, Vue will not behave as expected. More details can be found in this other answer.

(Nuxt) Vue component doesn't show up until page refresh

I'm storing nav items in my Vuex store and iterating over them for conditional output, in the form of a Vue/Bulma component, as follows:
<b-navbar-item
v-for='(obj, token) in $store.state.nav'
v-if='privatePage'
class=nav-link
tag=NuxtLink
:to=token
:key=token
>
{{obj.text}}
</b-navbar-item>
As shown, it should be output only if the component's privatePage data item resolves to true, which it does:
export default {
data: ctx => ({
privatePage: ctx.$store.state.privateRoutes.includes(ctx.$route.name)
})
}
The problem I have is when I run the dev server (with ssr: false) the component doesn't show up initially when I navigate to the page via a NuxtLink tag. If I navigate to the page manually, or refresh it, the component shows.
I've seen this before in Nuxt and am not sure what causes it. Does anyone know?
recommendation :
use mapState and other vuex mapping helper to have more readable code :).
dont use v-for and v-if at the same element
use "nuxt-link" for your tag
use / for to (if your addresses dont have trailing slash)
<template v-if='privatePage'>
<b-navbar-item
v-for='(obj, token) in nav'
class=nav-link
tag="nuxt-link"
:to="token" Or "`/${token}`"
:key="token"
>
{{obj.text}}
</b-navbar-item>
</template>
and in your script :
<script>
import {mapState} from 'vuex'
export default{
data(){
return {
privatePage: false
}
},
computed:{
...mapState(['privateRoutes','nav'])
},
mounted(){
// it's better to use name as a query or params to the $route
this.privatePage = this.privateRoutes.includes(this.$route.name)
}
}
</script>
and finally if it couldn't have help you , I suggest to inspect your page via dev tools and see what is the rendered component in html. it should be an <a> tag with href property. In addition, I think you can add the link address (that work with refresh and not by nuxt link) to your question, because maybe the created href is not true in navbar-item.
NOTE: token is index of nav array . so your url with be for example yourSite.com/1.so it's what you want?
This question has been answered here: https://stackoverflow.com/a/72500720/12747502
In addition, the solution to my problem was a commented part of my HTML that was outside the wrapper div.
Example:
<template>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
<div id='wrapper'> main content here </div>
</template>
Correct way:
<template>
<div id='wrapper'>
<!-- <div>THIS CREATES THE PROBLEM</div> -->
main content here
</div>
</template>

Multiple of the same component spawning the same modal on the same page?

I'm using Vue, and I have a component that outputs a button. That button opens a modal, also part of that component. This works.
If I have multiple of those buttons on the page then the modal is spawned twice. I understand why, but that's not what I want to happen.
Below is the code:
<template>
<div>
<button #click="$bvModal.show('edit-profile-modal')" class="btn btn-sm btn-secondary mt-3">Edit</button>
<b-modal id="edit-profile-modal" ref="editProfileModal" :ok-only="true">
<template #modal-title>
Edit your profile
</template>
<div class="d-block text-center">
</div>
</b-modal>
</div>
</template>
<script>
export default {
props: {
},
data() {
return {};
},
mounted() {
},
computed: {
},
methods: {
}
}
</script>
Is there a way to make this modal totally unique, so it isn't duplicated? It will always have the same content no matter what button is pressed.
The other questions on StackOverflow around this problem are focussed on the pattern of tabled data with 'Edit' buttons next to a row that spawns a modal with a form in it to edit that data - that's not what I'm trying to achieve. The modal is always the same and will always have the same data in it. I want to achieve a single component that I can drop in anywhere to allow a user to open this modal, so the solution isn't to put the modal outside of this component.
Thanks
I know this is a bit old, but I stumbled upon the question while fiddling around with Bootstrap Vue's Modals and thought I'd add in an alternate solution.
BvModal identifies templates by id, and when the show function is called, it will display ALL matching templates tagged with that ID. So knowing that, all we have to do is make the IDs unique!
A sneaky way of doing this is to utilise the uid that Vue assigns to it's components:
<template>
<div>
<button #click="$bvModal.show(`edit-profile-modal-${_uid}`)" class="btn btn-sm btn-secondary mt-3">Edit</button>
<b-modal :id="`edit-profile-modal-${_uid}`" ref="editProfileModal" :ok-only="true">
<template #modal-title>
Edit your profile
</template>
<div class="d-block text-center">
</div>
</b-modal>
</div>
Note that we're using template literals for both the $bvModal.show function and the id prop of the b-modal. You could just as easily use "'edit-profile-modal-' + _uid" if that looks nicer to you.
This will let you have any number of the same component on the page, without BvModal getting confused as to which modal belongs to which component and without having to create a custom component.
The modal must be in its own component, or it will inevitably be duplicated every time you write it in the template. Create a component for the modal which you can import just once into App.vue. You can toggle it with a Vuex state.
Modal.vue
<b-modal>
...
</b-modal>
App.vue
<template>
<div>
...
<Modal v-show="showModal" />
</div>
</template>
computed: {
showModal() {
return this.$store.state.showModal;
}
}
store.js
state: {
showModal: false
},
mutations: {
toggleModal(state, isShown) {
state.showModal = isShown;
}
}
Now from any component you can use the mutation to show the modal:
methods: {
showModal() {
this.$store.commit('toggleModal', true);
}
}

How to dynamically display an image in VueJS?

Please see here the relations between components
How to pass value from one child component to another in VueJS?
The snippet bellow not giving an error and not displaying the image
<img v-bind:src="image_url" />
Code:
<template>
<div>
<img v-bind:src="image_url" />
</div>
</template>
<script>
export default {
props: ["image_url"]
};
</script>
image_url comes from another component and in VueJS developer's tool I was able to confirm the value is an url. Looks like something wrong with the way how I'm trying to render a dynamic image.
You have to expose your images to your web server. Hopefully Vue-cli does that for you. You have to move your assets folder from src to the public folder.
More info on https://cli.vuejs.org/guide/html-and-static-assets.html#static-assets-handling
Don't forget to add.
<div v-if="image_url">
<img v-bind:src="image_url" />
</div>
Might worked on yours. Make a watcher for that to update again the props value an rerender it in your component.
export default {
props: ["image_url"],
watch: {
image_url(val){
this.image_url = val
},
}
};

Vue.js 2.0: Apply vue component rendered using v-html, compile the markup

I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>

Categories

Resources