I want to create a reusable wrapper function written in TypeScript for triggering a toast notification by using a composition function, as defined in the current specification for Vue 3.0: Composition API RFC.
This example is using BootstrapVue v2.0 toast component. With Vue 2, it would be invoked via the this.$bvToast
Vue component instance injection in the root context:
this.$bvToast.toast('Error happened', {
title: 'Oh no',
variant: 'danger'
});
This service-like composition function would look much like this:
// File: @/util/notify.ts
export function useNotify() {
const notifyError = (title: string, msg: string) => {
// How to access context.root as in a function component, without passing it to this function?
context.root.$bvToast.toast(msg, {
title,
variant: 'danger'
});
};
return { notifyError};
}
export default useNotify;
And would be used much like this:
// Use in your functional component:
import { createComponent } from '@vue/composition-api';
import { useNotify} from '@/util/notify';
export default createComponent({
name: 'MyFailingComponent',
setup() {
const { notifyError } = useNotify();
notifyError('Request error', 'There was an error processing your request, please try again later.');
return {};
}
});
Well, I soon found out a proper example on that same RFC site. But decided to share my examples here.
The RFC site doesn't include examples in TypeScript at the moment, for clarity's sake I presume. As this new way of writing Vue 3.0 components and composition functions (as a replacement to Mixins) takes a bit of getting used to.
Answer: You can pass the context object directly to the composition function when object-destructuring the needed parts into your component code.
// File: @/util/notify.ts
// import { SetupContext } from '@vue/composition-api';
export function useNotify({ root }) {
const notifyError = (title: string, msg: string) => {
root.$bvToast.toast(msg, {
title,
variant: 'danger'
});
};
return { notifyError };
}
export default useNotify;
// Use in your functional component:
import { createComponent, SetupContext } from '@vue/composition-api';
import { useNotify} from '@/util/notify';
export default createComponent({
name: 'MyFailingComponent',
setup(props: any, context: SetupContext) {
const { notifyError } = useNotify(context);
notifyError('Request error', 'There was an error processing your request, please try again later.');
return {};
}
});
Same using TypeScript types with complex object destructuring, when passing several function arguments as an object:
// File: @/util/notify.ts
import { SetupContext } from '@vue/composition-api';
export function useNotify({ context, defaultTitle = 'Hey!' }: { context: SetupContext, defaultTitle?: string }) {
const notifyError = (msg: string, title?: string) => {
context.root.$bvToast.toast(msg, {
title: title || defaultTitle,
variant: 'danger',
});
};
return {
notifyError,
};
}
export default useNotify;
// Usage like:
const { notifyError } = useNotify({ context });
// Or
const { notifyError } = useNotify({ context, defaultTitle: 'Hey there' });
Neat syntax, well done Vue community!