How to set different values from other component in vue 3? - vue.js

I am using a modal component from tailwindui. When using the vue code provided in a vue file (Modal.vue) and import this file in my base vue component (Home.vue) it works just fine. The modal will show up instantly which is fine. The example of the modal component is like this:
<script setup>
import { ref } from 'vue'
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '#headlessui/vue'
import { CheckIcon } from '#heroicons/vue/outline'
const open = ref(true)
</script>
When setting true to false, the modal is not being shown as expected. But how can i set open to true when i have included my modal in my Home.vue component like this:
<template>
<Modal/>
</template>
<script>
import Modal from '../Shared/Modal/Post';
export default {
components: {
Modal
},
}
</script>

As #Etheryte said you can use props.
Your home.vue
<template>
<Modal :isShow="true" />
</template>
<script>
import Modal from '../Shared/Modal/Post';
export default {
components: {
Modal
},
}
</script>
and your Modal.vue
<template>
...
</template>
<script>
export default {
props: {
isShow : {
type: Boolean,
default: false
},
}
</script>

Related

Vue Material Modal in Child Component is not showing when child component is imported to a parent component

I am having trouble in displaying the modal from a child component when I imported it into the parent component as tab content.
So first, the parent component looks something like this:
parent.vue
<template>
<div>
<nav-tabs-card no-label tabs-plain>
<template slot = "content">
<md-tabs>
<md-tab id="child" label="Child Component">
<child-component/>
</md-tab>
</md-tabs>
</template>
</nav-tabs-card>
</div>
</template>
<script>
import {NavTabsCard} from '#/components';
import childComponent from '../child-component.vue';
export default {
components: {
NavTabsCard,
"child-component": childComponent
}
}
</script>
then child-component.vue
<template>
<div>
<md-button #click = "myModalShow">
</md-button>
<modal v-if="myModal" #close = "myModalHide"/>
</div>
</template>
<script>
import {Modal} from '#/components';
export default {
components: {
Modal
},
data() {
return {
myModal: false
};
},
methods: {
myModalShow() {
this.myModal = true
console.log("Modal is visible");
},
myModalHide() {
this.myModal = false;
console.log("Modal is hidden.");
}
}
}
</script>
When I click the button to display the modal, the console.log returns that the myModal value is true, but the modal is not shown at all. I also tried to increase the modal's z-index but it does not work too.

Vue import component within functional component

I have a component called SpotifyButton in the components directory that looks like this:
<template functional>
<b-button pill size="sm" :href="props.spotifyUri" class="spotify-green">
<b-img-lazy
src="~/assets/Spotify_Icon_RGB_White.png"
height="20"
width="20"
/>
View on Spotify
</b-button>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'SpotifyButton',
props: {
spotifyUri: {
type: String,
required: true
}
}
});
</script>
I'm able to import and use this in a component in the pages directory like so without any problem:
<template>
<spotify-button :spotify-uri="artist.uri"/>
</template>
<script lang="ts">
import Vue from 'vue';
import { Context } from '#nuxt/types';
import FullArtist from '#/types/FullArtist';
import SpotifyButton from '#/components/SpotifyButton.vue';
export default Vue.extend({
name: 'ArtistPage',
components: {
SpotifyButton
},
async asyncData({ $axios, params, error }: Context) {
try {
const artist: FullArtist = await $axios.$get(`/api/artists/${params.id}`);
return { artist };
} catch (e) {
error({ statusCode: 404, message: 'Artist not found' });
}
},
data() {
return {
artist: {
name: ''
} as FullArtist
};
}
});
</script>
However if I try to import SpotifyButton into another component in the components directory in the same way, I get the following error.
Here is the ArtistPreview component, which is in the components directory:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
export default Vue.extend({
name: 'ArtistPreview',
components: {
SpotifyButton
},
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>
Am I missing something? Why does an import that works perfectly fine in a pages directory component not work in a components directory component?
This was happening because I'm using functional components. It turns out you can't nest functional components without doing some funky workarounds. Here's the GitHub issue with a few solutions.
I went with the first solution, so my ArtistPreview component now looks something like this:
<template functional>
<spotify-button :spotify-uri="props.artist.uri"/>
</template>
<script lang="ts">
import Vue, { PropType } from 'vue';
import SpotifyButton from '#/components/SpotifyButton.vue';
import SimpleArtist from '#/types/SimpleArtist';
Vue.component("spotify-button", SpotifyButton);
export default Vue.extend({
name: 'ArtistPreview',
props: {
artist: {
type: Object as PropType<SimpleArtist>,
required: true
}
}
});
</script>
Go with:
import SpotifyButton from '~/components/SpotifyButton.vue'
With Typescript is better use another approach: Add 'nuxt-property-decorator' and follow his flow.
So, you define your component as follow:
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import SpotifyButton from '~/components/SpotifyButton.vue'
#Component({
components: {
SpotifyButton
},
})
class AnotherComponent extends Vue {
...
}
export default AnotherComponent
</script>
[Nuxt Property Decorator on Github][1]
I think is important to read the official [Nuxt Typescript documentation][2] to a proper setup.
I hope it helps!
[1]: https://github.com/nuxt-community/nuxt-property-decorator
[2]: https://typescript.nuxtjs.org/

Hey! Do you know how to dynamically change the layout in Nuxt without store?

There is my default layout.
I want to change visibility on my header, when my modal is open, but I don't know how to change it dynamically. Any ideas?
// default.vue
<template>
<div class="container">
<header class="default-header">
<router-link class="logo" to="/"></router-link>
<div class="button-group">
<router-link to="/login" class="btn">Log in</router-link>
<router-link to="/register" class="btn">Sign up</router-link>
</div>
</header>
<nuxt />
</div>
</template>
//index.vue
<template>
<div>
<div #click="openModal">Open Modal</div>
<modal-popup v-model="showModal"></modal-popup>
</div>
</template>
<script>
import ModalPopup from "~/components/ModalPopup";
export default {
name: "Login",
components: {
ModalPopup
},
data() {
return {
showModal: false;
}
},
methods() {
openModal() {
this.showModal = true;
},
closeModal() {
this.showModal = false;
},
}
};
</script>
To do it without a store you can create an EventBus to trigger events which you can listen to. An eventbus provides communication between vue components.
You can create an eventbus js file.
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
Then in your components you can import it
import EventBus from '/eventbus'
In the index.vue component in your openModal() function you can change it to trigger the open modal event like this
openModal() {
EventBus.$emit('modal-opened', true);
this.showModal = true;
}
then in your default.vue component you can add a listener in the mounted function
mounted() {
EventBus.$on(‘modal-open’, function (payLoad) {
// Change header visibility here
});
}
Extra Note
If you don't want to import the Eventbus all the time you can import Eventbus in your app.js and just before your new Vue() you can add bus to the vue properties so your file will look something like this
import Vue from 'vue'
import EventBus from '/eventbus'
Object.defineProperties(
Vue.prototype,
{
$bus: {
get() => { return EventBus; }
}
}
)
new Vue({})
Then you can access the bus in your components like this
this.$bus.$emit('modal-open', true);
and
this.$bus.$on('modal-open', function(payload) {
})
Hope that helps

How to hide a paragraph using v-if through vuex?

I am trying to create a small app in which i can click on the button and hide the paragraph but i am trying to implement that using vuex.I have a paragraph in my Home.vue file and a button in my About.vue File. I want the paragraph hide conditionally in the click of the button but i want to accomplish that using vuex. How would i do that? My store.js, home.vue and About.vue are as follows.
This is how my store looks like.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
show:true,
},
mutations: {
toggle : state => {
state.show = !state.show
}
},
actions: {
}
})
This is the Home.vue file
<template>
<p>This needs to disappear</p>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
This is the About.vue file
<template>
<div>
<button #click="toggle">Click Me</button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
the mapMutations should be used in methods not in computed property :
methods:{
...mapMutations ([
"toggle"
])
}
like you see in the official docs :
You can commit mutations in components with this.$store.commit('xxx'), or use the mapMutations helper which maps component methods to store.commit calls (requires root store injection):

Vue-cli change object value globally

I have this code in file app.vue :
<template>
<div id="app">
<button v-on:click="component = 'login'">aa</button>
<component v-bind:is="component"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue'
import login from './components/login.vue'
export default {
name: 'app',
components: {
acceuil,
login
},
data(){
return {
component: 'acceuil'
}
}
}
</script>
How can I toggle between acceuil/login in component from a different vue file ?
You need to pass the imported dependency (the object or the name of the component as a string) to v-bind:is. You can do this by returning it in a computed function and pass it to a computed property, which you then can use in the template.
<template>
<div id="app">
<button v-on:click="isLogin = true">Show Login</button>
<component v-bind:is="currentComponent"></component>
</div>
</template>
<script>
import acceuil from './components/acceuil.vue';
import login from './components/login.vue';
export default {
name: 'app',
data () {
return {
isLogin: false
};
},
computed: {
currentComponent () {
return this.isLogin ? login : acceuil;
}
},
};
</script>
See also the documentation of dynamic components in the official docs.