any way to not use suspend in vue 3 for composition api async? - vue.js

I have a composition API to fetch data by Async in setup() but I should define like this in another component
<Suspense>
<template #default>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
if I don't use <Suspense>, that component not rendering in my app, how can i solve this?

You can achieve this by using v-if and v-else. You should also make the data reactive in case you haven't already done that.
<template>
<div v-if="data">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
</div>
<div v-else>
Loading...
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
export default defineComponent({
setup() {
const data = reactive({});
return {
data,
};
},
});
</script>

Related

How can I wrap every v-if in my Vue code in a transition?

The task of having to write
<transition name="fade">
<div v-if="condition">
</div>
</transition>
Is manual labour. Is there any shortcut way of wrapping every v-if with a transition?
You can create a custom Vue component:
// AppTransition.vue
<template>
<transition name='fade'>
<div v-if='show'>
<slot />
</div>
</transition>
</template>
<script>
export default {
props: {
show: Boolean
}
}
</script>
and then use it as follows:
<template>
<AppTransition :show='condition'>
<div>Hello, world</div>
</AppTransition>
</template>
<script>
// You can avoid importing it everywhere by making it a global component, see https://vuejs.org/guide/components/registration.html
import AppTransition from './AppTransition'
export default {
components: { AppTransition }
}
</script>

Display a specific route in a different router-view

I've got a Vue 3 app with Vue Router 4 and I want to achieve this:
<template>
<v-app>
<router-view></router-view> //This is where login would be
<app-layout>
<router-view /> //Everything else
</app-layout>
</v-app>
</template>
<script lang="ts" setup>
import AppLayout from "./components/AppLayout.vue";
</script>
I've got an <app-layout> component which includes navbar and a sidebar, but I don't want to display those when the user is not logged in. I also don't want to wrap each component in <app-layout> individually.
Is there a way I can achieve this?
You can use the Vue-router's v-slot API like this :
<template>
<v-app>
<router-view v-slot="{ Component, route }">
<component v-if="route.name === 'Login'" :is="Component" />
<app-layout v-else>
<component :is="Component" />
</app-layout>
</router-view>
<!-- Or -->
<router-view v-slot="{ Component, route }">
<component :is="route.name === 'Login' ? 'template' : 'app-layout'">
<component :is="Component" />
</component>
</router-view>
</v-app>
</template>
<script lang="ts" setup>
import AppLayout from "./components/AppLayout.vue";
</script>
And in your router, for the login route, don't forget to add the route name :
{
name: 'Login', // important
// ...
}

Vuejs 3 : components scope, and how to provide components to slots

I'd like in Vue 3 to be able to provide/overwrite existing components to slots by doing something like:
<ProvideFun>
<p>
Hello
<FunA></FunA>
</p>
</ProvideFun>
<ProvideOtherFun>
<p>
Hello
<FunA></FunA>
</p>
</ProvideOtherFun>
in order to be allowed to use some components (here <FunA>) inside others components (here <ProvideFun> and <ProvideOtherFun>). Note that ProvideFun and ProvideOtherFun may provide different versions of the <FunA> component.
How could I do something like that?
You can find a demo here.
Option 1: Provide/inject components
The parent (i.e., ProvideFun or ProvideOtherFun) could provide its own component definition (named "comp") to be used in FunA:
<!-- ProvideFun.vue -->
<script setup>
import { provide } from 'vue'
import CompA from './CompA.vue'
👇
provide('comp', CompA)
</script>
<template>
<h1>
Provide fun
</h1>
<slot />
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import { provide } from 'vue'
import CompB from './CompB.vue'
👇
provide('comp', CompB)
</script>
<template>
<h1>
Provide other fun
</h1>
<slot />
</template>
Make FunA.vue a <component> wrapper that injects the "comp" component definition from a parent:
<!-- FunA.vue -->
<script setup>
import { inject } from 'vue'
👇
const comp = inject('comp')
</script>
<template> 👇
<component :is="comp" />
</template>
This requires registering the FunA placeholder component before it could be used in ProvideFun/ProvideOtherFun:
<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
👇
import FunA from './FunA.vue'
</script>
<template>
<ProvideFun>
<p>
Hello
<FunA />
</p>
</ProvideFun>
<ProvideOtherFun>
<p>
Hello
<FunA />
</p>
</ProvideOtherFun>
</template>
demo 1
Option 2: Components via slot props
Bind the CompA component definition as a slot prop (named "FunA") in ProvideFun.vue, and CompB in ProvideOtherFun.vue:
<!-- ProvideFun.vue -->
<script setup>
import CompA from './CompA.vue'
</script>
<template> 👇
<slot :FunA="CompA">
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import CompB from './CompB.vue'
</script>
<template> 👇
<slot :FunA="CompB">
</template>
Then use a <component> to render it in the parent:
<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
</script>
<template>
<ProvideFun> 👇
<template v-slot="{ FunA }">
<p>
Hello 👇
<component :is="FunA" />
</p>
</template>
</ProvideFun>
<ProvideOtherFun> 👇
<template v-slot="{ FunA }">
<p>
Hello 👇
<component :is="FunA" />
</p>
</template>
</ProvideOtherFun>
</template>
demo 2

VueJS extends component with slots

I have a BaseModal Component like this :
<template>
<div class="base-modal" :class="{open: isOpen}">
<div class="modal">
<h2 class="modal-title">
<slot name="title"></slot>
</h2>
<div class="modal-content">
<slot name="content"></slot>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
#Component({})
export default class BaseModal extends Vue {
public isOpen: boolean = false;
// Some methods
}
</script>
I want to create an other modal component that will extend this one and pass it the content for the named slots. Here is where I am :
<template>
// How to give slots content here
</template>
<script lang="ts">
import BaseModal from "#app/components/BaseModal.vue";
import { Vue, Prop, Component } from "vue-property-decorator";
#Component({
extends: BaseModal,
})
export default class OtherDropdown extends Vue {
}
</script>
The extends is working but I can't figure out how to pass content to the named slot of the extended component. Not to have to write again all the BaseModal template.
Thanks
I'm using VueJS 2.6.8
You would want to use components named slots like this:
<template>
<BaseModal>
<template v-slot:title>
Some Title
</template>
<template v-slot:content>
Some content goes here
</template>
</BaseModal>
</template>
You can read more about named slots here: https://v2.vuejs.org/v2/guide/components-slots.html#Named-Slots
Hope this helps!

Can't Embed Vue Component into an md-app using Vue Material

I have the following...
PageHeader.vue
<template>
<span> ${message} </span>
</template>
<script>
export default{
data: function(){
return {message: "asdsad" };
}
}
</script>
app.vue
<template>
<jg-header></jg-header>
</template>
<script>
import PageHeader from "./PageHeader.vue"
export default{
components: {
"jg-header": PageHeader
}
}
</script>
This works great so I want to convert it to using Vue Material so I change app.vue to this...
<template>
<md-app> <jg-header></jg-header> </md-app>
</template>
<script>
import PageHeader from "./PageHeader.vue"
export default{
components: {
"jg-header": PageHeader
}
}
</script>
It seems to create and render the Vue material component, however, the custom component doesn't show up anywhere. How do I get the Vue component to actually render inside the md-app
Update
Since there was some confusion, I create the Vue app I do call Vue.use (hence why it renders)
Vue.use(VueMaterial);
new Vue(App).$mount("#my-id");
<md-app> has specific content elements. Wrap your content in <md-app-content>.
Instead of:
<template>
<md-app> <jg-header></jg-header> </md-app>
</template>
Do:
<template>
<md-app>
<md-app-content>
<jg-header></jg-header>
<md-app-content>
</md-app>
</template>
Or, with a toolbar:
<template>
<md-app>
<md-app-toolbar> ... </md-app-toolbar>
<md-app-content>
<jg-header></jg-header>
<md-app-content>
</md-app>
</template>
Try wrapping the tags inside template with a div :
<template>
<div>
<md-app>
<jg-header></jg-header>
</md-app>
</div>
</template>