import { debounce } from 'radash';
const onInput = (event) => {
console.log('onInput event', event);
}
const debouncedInput = debounce({ delay: debounceBy.value }, onInput);<form
@input.prevent="debouncedInput"
>
<!-- ... -->
</form>import { debounce } from 'radash';
const onInput = (event) => {
console.log('onInput event', event);
}
const debouncedInput = debounce({ delay: debounceBy.value }, onInput);<form
@input.prevent="debouncedInput"
>
<!-- ... -->
</form>| <script setup> | |
| import { ref, onMounted, nextTick } from 'vue'; | |
| const isLoading = ref(true); | |
| onMounted(async () => { | |
| // Simula tempo de carregamento | |
| await new Promise(resolve => setTimeout(resolve, 1500)); | |
| await nextTick(); | |
| isLoading.value = false; | |
| }); | |
| </script> | |
| <template> | |
| <div class="max-w-md mx-auto mt-12 p-4"> | |
| <!-- Skeleton loader --> | |
| <div v-if="isLoading" class="space-y-4 animate-pulse"> | |
| <div class="h-6 bg-gray-300 rounded w-1/2"></div> | |
| <div class="h-10 bg-gray-200 rounded"></div> | |
| <div class="h-6 bg-gray-300 rounded w-1/3 mt-6"></div> | |
| <div class="h-10 bg-gray-200 rounded"></div> | |
| <div class="h-12 bg-blue-300 rounded mt-4"></div> | |
| </div> | |
| <!-- Formulário real --> | |
| <form v-else class="bg-white shadow rounded p-6 space-y-4"> | |
| <h2 class="text-xl font-bold">Formulário</h2> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Nome</label> | |
| <input type="text" class="border border-gray-300 p-2 rounded w-full" /> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-1">Email</label> | |
| <input type="email" class="border border-gray-300 p-2 rounded w-full" /> | |
| </div> | |
| <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Enviar</button> | |
| </form> | |
| </div> | |
| </template> |
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
<template>
<template
v-if="!slots.default"
>
<button type="button">button</button>
</template>
<template v-else>
<button type="button"><slot/></button>
</template>
</template>Para acessar slots e atributos (attrs) usando a Composition API no Vue.js, você precisa entender como a função setup funciona e como ela fornece acesso ao contexto do componente. A Composition API introduziu uma nova maneira de acessar e manipular slots e atributos, diferente da Options API, que usava o objeto this para acessar esses recursos.
Na Composition API, o contexto do componente, que inclui slots e atributos, é passado como o segundo argumento para a função setup. Você pode acessar slots e atributos diretamente através desse objeto de contexto. Aqui estão exemplos de como fazer isso:
export default {
setup(props, context) {
console.log(context.slots);
console.log(context.attrs);
}
}export default {
setup(props, { slots, attrs }) {
console.log(slots);
console.log(attrs);
}
}Os slots são espaços reservados em um componente Vue.js que permitem que o conteúdo seja passado de um componente pai para um componente filho. Eles são definidos dentro do bloco <template> de um componente Vue.js e podem ser usados para injetar conteúdo dinâmico.
<!-- MyButton.vue -->
<template>
<button>
<slot></slot>
</button>
</template>
<!-- Parent of button -->
<template>
<my-button>
This is going to be placed where the slots are <b>Also accept HTML and components</b>
</my-button>
</template>Para componentes que usam a sintaxe <script setup>, a Composition API fornece composables useSlots e useAttrs para acessar slots e atributos.
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>A Composition API oferece uma maneira poderosa e flexível de acessar e manipular slots e atributos em componentes Vue.js. Ao usar a função setup e os composables useSlots e useAttrs, você pode criar componentes mais dinâmicos e reutilizáveis, aproveitando ao máximo os recursos do Vue.js [1][2][3][4][5].
Citations: [1] https://dev.to/zelig880/how-to-use-slots-and-attrs-with-the-composition-api-k75 [2] https://learnvue.co/articles/vue-context-argument [3] https://stackoverflow.com/questions/60202724/vue-3-composition-api-and-access-to-vue-instance [4] https://vuejs.org/api/composition-api-setup.html [5] https://vuejs.org/guide/components/slots.html [6] https://doc.vueframework.com/api/composition-api.html [7] https://vue-community.org/vue/comprehensive-guide-to-vue-js-slots-mastering-component-composition.html [8] https://www.stackovercloud.com/2021/12/03/how-to-make-your-vue-js-application-dry-with-slots-mixins-and-composition-api/ [9] https://v2.vuejs.org/v2/guide/components-slots.html [10] https://markus.oberlehner.net/blog/context-and-provider-pattern-with-the-vue-3-composition-api/ [11] https://www.webmound.com/vue-context-argument-composition-api-script-setup/ [12] https://vuejs.org/guide/extras/render-function.html [13] https://www.nightprogrammer.com/vue-3/how-to-use-slots-in-composition-api-in-vue-js-3-example/ [14] https://staging-cf.vuejs.org/guide/components/slots [15] https://medium.com/dana-engineering/complete-guide-vue-3-composition-api-284e19ee0831
<template>
<div>
<p>Count: {{ count }}</p>
<!-- <ChildComponent /> -->
</div>
</template>
<script lang="ts">
import { computed, defineComponent, provide } from 'vue'
// import ChildComponent from './ChildComponent.vue'
export default defineComponent({
components: {
// ChildComponent,
},
setup() {
const count = computed(() => {
return 1 + 1
})
provide('count', count)
return {
count,
}
},
})
</script>
// so-carrao-projetos/the-spa/components/molecules/ecommerce/AdvertisementStepper.vue
export default {
name: 'AdvertisementStepper',
components: {
StepOne: () => import(/* webpackMode: "eager" */'@/components/molecules/ecommerce/steps/StepOne'),
StepTwo: () => import(/* webpackMode: "eager" */'@/components/molecules/ecommerce/steps/StepTwo'),
StepThree: () => import(/* webpackMode: "eager" */'@/components/molecules/ecommerce/steps/StepThree'),
StepFour: () => import(/* webpackMode: "eager" */'@/components/molecules/ecommerce/steps/StepFour'),
StepFive: () => import(/* webpackMode: "eager" */'@/components/molecules/ecommerce/steps/StepFive')
},
// ...
}// resources/js/Components/Icons/icons.js
/*
// Usage:
app.use(loadIcons())
*/
import EyeIcon from '@/Components/Icons/EyeIcon.vue';
import EyeSlashIcon from '@/Components/Icons/EyeSlashIcon.vue';
export const loadIcons = () => {
return {
install(app, options) {
app
.component('EyeIcon', EyeIcon)
.component('EyeSlashIcon', EyeSlashIcon);
},
}
}Here's an example of a Vue.js plugin that provides both global functions and directives. This plugin exposes a $hello() function and a v-my-directive directive.
// extra/plugins/AppExtra/index.js
const toJson = function (...params) {
return JSON.stringify(...params);
};
const AppExtra = {
install(app, options) {
const methods = {
toJson,
}
app.mixin({
methods: methods,
});
if (parseInt(app.version) > 2) {
Object.entries(methods).forEach(item => {
let [name, func] = item;
app.provide(name, func);
})
}
// Global function
app.config.globalProperties.$toJson = toJson;
// Global directive
app.directive('extra-data', {
beforeMount(el, binding, vnode, prevVnode) {
// el.textContent = binding.value;
el.setAttribute('data-extra', JSON.stringify(binding.value));
},
updated(el, binding, vnode, prevVnode) {
el.setAttribute('data-extra', JSON.stringify(binding.value));
// el.textContent = binding.value;
},
});
},
};
export default AppExtra;To use this plugin, you would import it and call app.use() in your Vue app's initialization:
// main.js
import { createApp } from 'vue';
import AppExtra from '@/extra/plugins/AppExtra';
const app = createApp({});
app.use(AppExtra);
app.mount('#app');Now, you can use the $hello() function and v-my-directive directive in your components:
<!-- App.vue -->
<template>
<div>
<p v-extra-data="{abc: 'def', ghi: 123, xyz: true}"></p>
<h1>{{ $toJson({abc: 'def', ghi: 123, xyz: true}) }}</h1>
<h1>{{ toJson({abc: 'def', ghi: 123, xyz: true}) }}</h1>
</div>
</template>This example demonstrates how to create and use a simple plugin that provides both global functions and directives. You can extend this plugin to include more functionality as needed.
For more information, you can refer to the following resources:
<script setup lang="js">
const vMyDirective = {
beforeMount: (el) => {
el.innerText = el.innerText.toUpperCase();
}
}
</script>
<span v-my-directive>algo aqui para teste</span>// myDirective.js
export const myDirective = {
beforeMount: (el) => {
el.innerText = el.innerText.toUpperCase();
}
}<script setup lang="js">
import { myDirective as vMyDirective } from './myDirective.js'
</script>
<span v-my-directive>algo aqui para teste</span>
<!--
Resultado:
<span>ALGO AQUI PARA TESTE</span>
-->// resources/js/plugins/lang.js
import { lang } from '@/helpers/localization';
// Criação do plugin
export const LangPlugin = {
install(app, options) {
const _lang = (...params) => lang(...params);
app.mixin({
methods: {
lang: _lang,
},
});
if (parseInt(app.version) > 2) {
app.provide('lang', _lang);
}
},
};// resources/js/app.js
import { LangPlugin } from '@/plugins/lang';
// ...
createApp(...)
.use(LangPlugin)
// ...import { router } from "@inertiajs/vue3";
router.visit(route('dashboard', ['aaa', { tab: e }])); // { tab: '1' } is the query parameter<script>
import { router } from "@inertiajs/vue3";
const handleTabChange = (tab) => {
let userId = 1;
router.visit(route('dashboard', [ // .../dashboard?abc=&tab=tab1&user=1
'abc',
{ tab: tab },
{ user: userId },
])
);
}
</script>
<template>
<button
type="button"
@click="handleTabChange('tab1')"
>Go to tab1</button>
</template>3 Ways to Persist Pinia State (on local storage)
<!DOCTYPE html>
<html>
<head>
<title>Toggle Message</title>
<script src="https://unpkg.com/vue@3.4.18/dist/vue.global.js"></script>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!-- Add missing attributes to a and p elements -->
<a href="#" @click.prevent="toggle">Want to buy a new car?</a>
<p v-cloak v-show="isVisible">Call {{phoneNumber}}</p>
</div>
<script>
const appConfig = {
data() {
return {
isVisible: false,
phoneNumber: '+11 22 33 44 now!',
}
},
methods: {
toggle() {
this.isVisible = !this.isVisible;
}
}
};
const app = Vue.createApp(appConfig).mount('#app');
</script>
</body>
</html>