How to use composition API to create a new component in vue3? - vuejs2

When we use vue2 to create API, we just follow options API like below:
data are in data
methods are in methods
<script>
export default {
name: 'demo',
components: {},
filter:{},
mixins:{},
props: {},
data(){
return{
}
},
computed:{},
watch:{},
methods: {},
}
</script>
But the vue3 changed, how should I build a component with vue3 composition API?
Some example say that I should import reactive etc. From vue first and put all codes in setup(){}?
Some example show that I can add setup to <script>?
Please give me an example.

ok bro , Composition Api works like that:
<script>
import { fetchTodoRepo } from '#/api/repos'
import {ref,onMounted} from 'vue'
export default {
setup(props){
const arr = ref([]) // Reactive Reference `arr`
const getTodoRepo = async () => {
arr.value = await fetchTodoRepo(props.todo)
}
onMounted(getUserRepo) // on `mounted` call `getUserRepo`
return{
arr,
getTodoRepo
}
}
}
</script>

There are two ways to create a component in vue3.
One:<script> + setup(){},such as this:
<script>
import { reactive, onMounted, computed } from 'vue'
export default {
props: {
title: String
},
setup (props, { emit }) {
const state = reactive({
username: '',
password: '',
lowerCaseUsername: computed(() => state.username.toLowerCase())
})
onMounted(() => {
console.log('title: ' + props.title)
})
const login = () => {
emit('login', {
username: state.username,
password: state.password
})
}
return {
login,
state
}
}
}
</script>
Two:use <script setup="props">
loading....

Related

How can I pass the arguments to the NUXT component in Storybook?

I've got a NUXT/VUE component which does an API call and assigns the result to the data, therefore template maps the result to the page. Simple API call.
I am just not sure how I can do that in Storybook component?
Do I have to mock the API fetch or pass the static data to the component in the Storybook?
Examples on the the official website is all about props, nothing about data
https://storybook.js.org/docs/react/writing-stories/args
Here is my simple component
<template>
<div>{{blogPosts.title}}</div>
</template>
<script>
export default {
data() {
return {
blogPosts: [],
};
},
async fetch() {
this.blogPosts = await this.$http.$get("https://api.nuxtjs.dev/posts");
},
};
</script>
Here is my Storybook Component:
import { Meta, Story } from "#storybook/vue";
import BlogCarousel from "./BlogCarousel.vue";
import { BlogPost } from "~/lib/types/BlogPost";
export default {
title: "BlogCarousel",
components: BlogCarousel,
} as Meta;
const Template: Story<BlogPost> = (args) => {
return {
components: { BlogCarousel },
template: `<BlogCarousel v-bind=${args.blogPosts} />`,
};
};
export const Default = Template.bind({});
Default.args = {
blogPosts: [
{
title: "test",
created: "today",
},
],
};

VUE 3 JS : can't acces to my props in mounted

I have a problem in a component.
I receive an id (name : theIdPost) from a parent file of this component but when I would like to use it in the mounted(){} part , it tells me :
TS2339: Property 'theIdPost' does not exist on type '{...
I can print the id in template, no worries but to use it in the SCRIPT part it doesn't work.
the component file:
<template lang="fr">
// All my html
</template>
<script lang="ts">
import { computed } from 'vue';
import { store } from '../store/index';
export default{
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup() {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
return { commentList };
},
mounted() {
const myStore: any = store;
myStore.dispatch("getComments",
{'id': this.theIdPost}
);
}
}
</script>
<style lang="scss">
#import "../scss/variables.scss";
// ..... the style part
</style>
Can you explain me why it doesn't work ?
Thanks
If you are using the composition API with the setup, you have to add the lifecycle hooks differently:
https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
For Solution there is 2 points :
because I use vue 3 and setup in composition API , the lifecycle Hook is different and mounted => onMounted
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
when we use onMounted, is like when we use ref(), we have to import before. So at the beginning of the SCRIPT part, we have to write :
import { onMounted } from 'vue';
So my final script is :
<script lang="ts">
import { computed, onMounted } from 'vue';
import { store } from '../store/index';
export default {
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup(props) {
const myStore: any = store;
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{ 'id': props.theIdPost }
);
})
return { commentList };
},
}
</script>
Thanks to Thomas for the beginning of the answer :)
it worked for me too. i was setting up the setup and not pass props in to the setup. now okay

Vue3 reactive components on globalProperties

In vuejs 2 it's possible to assign components to global variables on the main app instance like this...
const app = new Vue({});
Vue.use({
install(Vue) {
Vue.prototype.$counter = new Vue({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ },
}
});
}
})
app.$mount('#app');
But when I convert that to vue3 I can't access any of the properties or methods...
const app = Vue.createApp({});
app.use({
install(app) {
app.config.globalProperties.$counter = Vue.createApp({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ }
}
});
}
})
app.mount('#app');
Here is an example for vue2... https://jsfiddle.net/Lg49anzh/
And here is the vue3 version... https://jsfiddle.net/Lathvj29/
So I'm wondering if and how this is still possible in vue3 or do i need to refactor all my plugins?
I tried to keep the example as simple as possible to illustrate the problem but if you need more information just let me know.
Vue.createApp() creates an application instance, which is separate from the root component of the application.
A quick fix is to mount the application instance to get the root component:
import { createApp } from 'vue';
app.config.globalProperties.$counter = createApp({
data: () => ({ value: 1 }),
methods: {
increment() { this.value++ }
}
}).mount(document.createElement('div')); 👈
demo 1
However, a more idiomatic and simpler solution is to use a ref:
import { ref } from 'vue';
const counter = ref(1);
app.config.globalProperties.$counter = {
value: counter,
increment() { counter.value++ }
};
demo 2
Not an exact answer to the question but related. Here is a simple way of sharing global vars between components.
In my main app file I added the variable $navigationProps to global scrope:
let app=createApp(App)
app.config.globalProperties.$navigationProps = {mobileMenuClosed: false, closeIconHidden:false };
app.use(router)
app.mount('#app')
Then in any component where I needed that $navigationProps to work with 2 way binding:
<script>
import { defineComponent, getCurrentInstance } from "vue";
export default defineComponent({
data: () => ({
navigationProps:
getCurrentInstance().appContext.config.globalProperties.$navigationProps,
}),
methods: {
toggleMobileMenu(event) {
this.navigationProps.mobileMenuClosed =
!this.navigationProps.mobileMenuClosed;
},
hideMobileMenu(event) {
this.navigationProps.mobileMenuClosed = true;
},
},
Worked like a charm for me.
The above technique worked for me to make global components (with only one instance in the root component). For example, components like Loaders or Alerts are good examples.
Loader.vue
...
mounted() {
const currentInstance = getCurrentInstance();
if (currentInstance) {
currentInstance.appContext.config.globalProperties.$loader = this;
}
},
...
AlertMessage.vue
...
mounted() {
const currentInstance = getCurrentInstance();
if (currentInstance) {
currentInstance.appContext.config.globalProperties.$alert = this;
}
},
...
So, in the root component of your app, you have to instance your global components, as shown:
App.vue
<template>
<v-app id="allPageView">
<router-view name="allPageView" v-slot="{Component}">
<transition :name="$router.currentRoute.name">
<component :is="Component"/>
</transition>
</router-view>
<alert-message/> //here
<loader/> //here
</v-app>
</template>
<script lang="ts">
import AlertMessage from './components/Utilities/Alerts/AlertMessage.vue';
import Loader from './components/Utilities/Loaders/Loader.vue';
export default {
name: 'App',
components: { AlertMessage, Loader }
};
</script>
Finally, in this way you can your component in whatever other components, for example:
Login.vue
...
async login() {
if (await this.isFormValid(this.$refs.loginObserver as FormContext)) {
this.$loader.activate('Logging in. . .');
Meteor.loginWithPassword(this.user.userOrEmail, this.user.password, (err: Meteor.Error | any) => {
this.$loader.deactivate();
if (err) {
console.error('Error in login: ', err);
if (err.error === '403') {
this.$alert.showAlertFull('mdi-close-circle', 'warning', err.reason,
'', 5000, 'center', 'bottom');
} else {
this.$alert.showAlertFull('mdi-close-circle', 'error', 'Incorrect credentials');
}
this.authError(err.error);
this.error = true;
} else {
this.successLogin();
}
});
...
In this way, you can avoid importing those components in every component.

How can I bind data/methods in Vue using the Composition API using a "render" function?

Is there a way to bind (and expose in the component itself) "data"/"methods" using the Composition API in using a "render" function (and not a template) in Vue.js?
(Previously, in the Options API, if you use a render method in the options configuration, all of the data/props/methods are still exposed in the component itself, and can be still accessed by componentInstance.someDataOrSomeMethod)
Templated Component:
<template>
<div #click="increment">{{ counter }}</div>
</template>
<script lang="ts">
import { defineComponent, Ref, ref, computed } from '#vue/composition-api'
export default defineComponent({
name: 'TranslationSidebar',
setup () {
const counter: Ref<number> = ref(0)
const increment = () => {
counter.value++
}
return {
counter: computed(() => counter.value),
increment
} // THIS PROPERTY AND METHOD WILL BE EXPOSED IN THE COMPONENT ITSELF
}
})
</script>
Non-Templated "Render" Component:
<script lang="ts">
import { defineComponent, Ref, ref, createElement } from '#vue/composition-api'
export default defineComponent({
name: 'TranslationSidebar',
setup () {
const counter: Ref<number> = ref(0)
const increment = () => {
counter.value++
}
return () => {
return createElement('div', { on: { click: increment } }, String(counter.value))
} // THE COUNTER PROP AND INCREMENT ARE BOUND, BUT NOT EXPOSED IN THE COMPONENT ITSELF
}
})
</script>
Options API using the render option:
<script lang="ts">
export default {
name: 'Test',
data () {
return {
counter: 0
}
},
mounted () {
console.log('this', this)
},
methods: {
increment () {
this.counter++
}
},
render (h) {
return h('div', { on: { click: this.increment } }, this.counter)
}
}
</script>
I can't make any claim that this is the proper way as it seems pretty hacky and not really ideal, but it does do what I need to do for now. This solution uses the provide method which grants access to properties/methods provided via componentInstance._provided. Not really ecstatic doing it this way though:
<script lang="ts">
import { defineComponent, Ref, ref, createElement, provide, computed } from '#vue/composition-api'
export default defineComponent({
name: 'TranslationSidebar',
setup () {
const counter: Ref<number> = ref(0)
const increment = () => {
counter.value++
}
provide('increment', increment)
provide('counter', computed(() => counter.value))
return () => {
return createElement('div', { on: { click: increment } }, String(counter.value))
}
}
})
</script>

AngularJS services in Vue.js

I'm new to Vue.js and looking for the equivalent of a service in AngularJS, specifically for storing data once and getting it throughout the app.
I'll be mainly storing the results of network requests and other promised data so I don't need to fetch again on very state.
I'm using Vue.JS 2.0 with Webpack.
Thanks!
I think what u are seeking for is vuex, which can share data from each component.
Here is a basic demo which from my code.
store/lottery.module.js
import lotteryType from './lottery.type'
const lotteryModule = {
state: {participantList: []},
getters: {},
mutations: {
[lotteryType.PARTICIPANT_CREATE] (state, payload) {
state.participantList = payload;
}
},
actions: {
[lotteryType.PARTICIPANT_CREATE] ({commit}, payload) {
commit(lotteryType.PARTICIPANT_CREATE, payload);
}
}
};
export default lotteryModule;
store/lottery.type.js
const PARTICIPANT_CREATE = 'PARTICIPANT_CREATE';
export default {PARTICIPANT_CREATE};
store/index.js
Vue.use(Vuex);
const store = new Vuex.Store();
store.registerModule('lottery', lotteryModule);
export default store;
component/lottery.vue
<template>
<div id="preparation-container">
Total Participants: {{participantList.length}}
</div>
</template>
<script>
import router from '../router';
import lotteryType from '../store/lottery.type';
export default {
data () {
return {
}
},
methods: {
},
computed: {
participantList() {
return this.$store.state.lottery.participantList;
}
},
created() {
this.$store.dispatch(lotteryType.PARTICIPANT_CREATE, [{name:'Jack'}, {name:'Hugh'}]);
},
mounted() {
},
destroyed() {
}
}
</script>
You don't need Vue-specific services in Vue2 as it is based on a modern version of JavaScript that uses Modules instead.
So if you want to reuse some services in different locations in your code, you could define and export it as follows:
export default {
someFunction() {
// ...
},
someOtherFunction() {
// ...
}
};
And then import from your Vue code:
import service from 'filenameofyourresources';
export default {
name: 'something',
component: [],
data: () => ({}),
created() {
service.someFunction();
},
};
Note that this is ES6 code that needs to be transpiled to ES5 before you can actually use it todays browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export