How can Quasar Dialog be mounted under root or parent component? - vue.js

The dynamically created Dialog is mounted under the <body> tag by default, so that the global Provide and Inject cannot be used. How can Dialog be mounted under div#app or mounted under parent ?

It is always mounted either under <body> (non-SSR) or nowhere (SSR mode) - not just "by default". Just look at the sources - https://github.com/quasarframework/quasar/blob/dev/ui/src/utils/private/global-nodes.js#L6

You can setup the provide on the Vue app, using app.provide() instead of an <App/> component. For example, in the entry script
import { createApp } from 'vue'
import { Quasar, Dialog } from 'quasar'
import App from './App.vue'
createApp(App)
.provide('name', value)
.use(Quasar, {
plugins: {
Dialog,
}
})
.mount('#app')
Then you will be able to use inject() the same item in Quasar dialog children, even though the dialog is mounted under <body>.

Related

Ionic Vue 3 application switch tab from another tab

I have created a ionic vue 3 tabs starter application.
Im trying to to switch tab programmatically from one tab to another
here is my situation
// in Tab1.vue page
setup(props, context) {
function moveToTab3(){
// here I need the code to switch tab1 to tab3
// possible to call tabs.select() method here ?
}
}
// my Tabs.vue page
<ion-tabs ref="tabs" >
I searched in the ionic docs and vue docs to know how to get the parent component from a child component, I have not got a solution yet.
any help is much appreciated, Thank you so much
yes you can absolutely do that programmatic routing provided you have access to Vue-Router because ionic under the hood uses Vue router for navigation and since tabs are top-level navigation you can simply call
$router.push OR $router.replace inside of your setup function on button click or bind the tab using router-link
Here is the documentation from Ionic related to Navigation/Routing Link And
I think this is what you are looking for Accessing The Ionrouter Instance Link
=====Update =====
Taken directly from the documentation Link, As you can see in the template on Ion-Button a simple #click is used to push the route you wish to navigate, while in script tag useRouteris accessed from the core vue-router to get access to the underlying router
<template>
<ion-page>
<ion-content>
<ion-button #click="() => router.push('/detail')">Go to detail</ion-button>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { IonButton, IonContent, IonPage } from '#ionic/vue';
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
export default defineComponent({
name: 'HomePage',
components: {
IonButton,
IonContent,
IonPage
},
setup() {
const router = useRouter();
return { router };
}
})
</script>
The same link also shows how you use router-link on Ion-Button
<ion-button router-link="/detail">Go to detail</ion-button>
without having to tap into a method.. either way, works...

PhpStorm replaced vuejs separated script tag

When I add a tag to a new component in the Vue.js template, PhpStorm automatically replace my separated script tag. I know you don't understand what I just wrote. Here an example of what happened.
My original App.vue looks like this:
<template>
<my-other-component></my-other-component>
<template>
// Note the script link to a separate Ts file.
<script src="./App.ts"></script>
App.ts file:
import { Vue } from 'vue-property-decorator'
import { Component } from 'vue-property-decorator'
#Component()
export default class App extends Vue {
}
Everything is fine from here.
The problem comes when I add a tag to another new component in the template, and let PhpStorm handle the import, it replace my script tag to this:
<script>
import Vue from 'vue'
import { Component } from 'vue-class-component'
import AttributeBindingSelector from '#/components/Properties/Types/AttributeBindingSelector'
#Component({
components: { AttributeBindingSelector }
})
export default class App extends Vue {
}
</script>
Note the script tag generated by PhpStorm replaced my <script src="./App.ts"></script> instead of importing the component into my ./App.ts typescript file.
Here is a video about what is happening: https://drive.google.com/file/d/1Kv7R3WKToWjAlCRyGNQjm_UNlLWTezXV/view?usp=sharing

Is it possible to render a component automatically in vue 3?

I'm working on the small library for showing notifications/toasts for vue 3.
My idea is to append an invisible container for my notifications during the plugin registration. So end user should not care about rendering this area. Is it possible at all?
My current plugin look like this:
export const plugin = {
install: (app: App, options?) => {
options = reactive(options || defaultOptions);
app.provide(symbol, instance);
app.component('vue3-notification', Notification);
app.component('vue3-notifications', Overlay);
console.log('app', app); // app._component is null at this point
var test = Overlay.render({ notifications: instance });
console.log('test', test); // how to attach Overlay component to app?
}
};
It seems like when a plugin is installed the vue root container is not available yet. I managed to render my component providing needed dependency(at least I hope so, it's logged to the console in the last line) but I don't know how to mount it and integrate with the main app.
My overlay component that I want to render automatically from plugin look like this:
<div class="notifications-overlay">
<Teleport to="body">
<vue3-notification
v-for="(n, index) in notifications.stack.value"
:key="n.id"
v-bind="n"
v-bind:hide="() => hide(n.id)"
></vue3-notification>
</Teleport>
</div>
And it has fixed position:
.notifications-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
}
So it does not matter where it's rendered exactly, I just want to be it automatically available inside the vue app after using my plugin.
Any thoughts?
In Vue 2, we have Vue.extend for creating a "subclass" of the base Vue constructor, and it also allows you to mount the instance on an element, which is great for this purpose.
However, this API has been removed in Vue 3. Have a read on the RFC for global API changes.
Since the global Vue is no longer a new-able constructor, Vue.extend no longer makes sense in terms of constructor extension.
The good news is, we can still achieve pretty much the same goal by leveraging createApp to render our plugin component and mount it to a DOM element.
If you don't like the idea of instantiating multiple Vue instances, you may want to check out this unofficial library called mount-vue-component. I haven't tried it myself, but it allows you to mount a component without using createApp. Although, it seems to use some internal properties (like _context) to get things done. I'd say whatever is undocumented, is likely to change. But hey.
So, back to the createApp approach. And we won't be using Teleport here. The following steps are just my preferences, so feel free to adjust them according to your use case.
Adding interfaces
import { ComponentPublicInstance } from 'vue';
export interface INotify {
(message: string): void;
}
export type CustomComponentPublicInstance = ComponentPublicInstance & {
notify: INotify;
}
We are using intersection type for our custom component instance.
Plugin implementation
import { App, createApp } from 'vue';
import Notifier from './path/to/component/Notifier.vue';
export const injectionKeyNotifier = Symbol('notifier');
export default {
install(app: App) {
const mountPoint = document.createElement('div');
document.body.appendChild(mountPoint);
const notifier = createApp(Notifier).mount(mountPoint) as CustomComponentPublicInstance;
app.provide(injectionKeyNotifier, notifier.notify);
}
}
At this point, we simply need to expose a public method (see INotify above) from the target component (Notifier.vue). I'm calling this method notify. And it takes a string argument for the message.
The component: Notify.vue
<template>
<div class="my-notifier">
<div class="msg" v-text="message"></div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent(() => {
const message = ref('');
function notify(msg: string) {
message.value = msg;
}
return {
message,
notify
}
})
</script>
Notice a named function called notify. This is the public method we talked about earlier and we'll need it exported.
Now use() it
On your entry file (e.g. main.ts):
import { createApp } from 'vue'
import App from './App.vue'
import Notifier from 'my-custom-notifier'; // Assumes a library from the NPM registry (if you mean to publish it)
createApp(App)
.use(Notifier)
.mount('#app');
Example usage that displays random notification:
<template>
<div class="home">
<button #click="showNotifs">Show notification</button>
</div>
</template>
<script lang="ts">
import { defineComponent, inject } from 'vue';
import { INotify, injectionKeyNotifier } from 'my-custom-notifier';
export default defineComponent({
name: 'Home',
setup() {
const notify = inject(injectionKeyNotifier) as INotify;
function showNotifs() {
notify('You have x unread messages.');
}
return {
showNotifs
}
}
})
</script>
And that's it! We are auto-registering the component without our users having to manually add it on the template.

Vue.Js - Import components problem in App.Vue Component

I have a problem with my VUE js project. I use the library VueperSlides and it's run correctly but my problem, is that the slider is imported in the components App.vue and it's displayed. I want to display the slider only in my Slider component.
I have a router link which runs correctly.
This is the APP.VUE component
This the HomePage (I want to put away this slider )
This is the Slider Vue
Just import it on your HomePage.vue:
import { VueperSlides, VueperSlide } from "vueperslides";
import "vueperslides/dist/vueperslides.css";
export default {
components: {
VueperSlides,
VueperSlide
},
// ...
}
Then use vueper-slides and vueper-slide components on your template.

How to enable disable component in a complex vue app?

I am just new to vue and working on this example Notes application, it can be found below
https://coligo.io/learn-vuex-by-building-notes-app/
My question is how can I change component seen on screen right behind the toolbar. I have read basic guide but I am still behind it.
Lets say I have a button on left side call it message and have another component listing like NotesList and its called MessagesList. So, When I click message button, I want NotesList dissappear and MessageList come instead of that, and vice versa. I do not want NotesList stay as consistent on screen.
Here is App.vue and main.js files, please look in github page for mode code.
https://github.com/coligo-io/notes-app-vuejs-vuex
<template>
<div id="app">
<toolbar></toolbar>
<notes-list></notes-list>
<editor></editor>
</div>
</template>
<script>
import Toolbar from './Toolbar.vue'
import NotesList from './NotesList.vue'
import Editor from './Editor.vue'
import MessageList from './MessageList.vue'
export default {
components: {
Toolbar,
NotesList,
Editor,
MessageList
}
}
</script>
Main.js
import Vue from 'vue'
import store from './vuex/store'
import App from './components/App.vue'
new Vue({
store, // inject store to all children
el: 'body',
components: { App }
})
You could do this either by using v-if on each of the components, or by using dynamic components. In either case, your button should toggle a data value which would be used to control which component is displayed.