How to use uploadAreaMask slot component in vue formulate in Nuxt - vue-formulate

I tried by configuring my own component as below in plugins. But my component haven't overrided default component.Please help.
import Vue from "vue";
import VueFormulate from "#braid/vue-formulate";
import FileUploadArea from "~/components/app/FileUploadArea";
// Register your component with vue
Vue.component("FileUploadArea", FileUploadArea);
// Let Vue Formulate know which slot you want to override for a given type
Vue.use(VueFormulate, {
library: {
// the `type` of input you’re targeting.
file: {
slotComponents: {
uploadAreaMask: "FileUploadArea",
},
},
},
});

Related

How can i make all v-text-field components outlined by default in nuxt/vuetify module

i'm using the nuxt/vuetify module and would like to make all v-text-fields components outlined.
Try to create and register plugin which register new vue component, that extends vuetify VTextField component.
import Vue from 'vue';
import { VTextField } from "vuetify/lib"
Vue.component('mTextField', {
extends: VTextField,
props: {
outlined: {
type: Boolean,
default: true
}
}
})
But always catch error while try to use mTextField component
Unexpected token 'export'
How can i make all v-text-fields components outlined?
Add transpile section in nuxt.config.js with 'vuetify/lib' worked for me
build: {
transpile: ['vuetify/lib']
},

Vue 2: How to unit test component that uses Chart.js (vue-chart-3)

I have a vue2 project that uses ClassComponents and Chart.js (via vue-chart-3). I now have a simple component that wraps a DoughnutChart to manage data and stuff.
DBOverviewDoughnut.vue
<template>
<div>
<p>test</p>
<DoughnutChart ref="doughnutRef" :chartData="sharesData"></DoughnutChart>
</div>
</template>
<script lang="ts">
import Component from 'vue-class-component';
import Vue from 'vue';
import { DoughnutChart, ExtractComponentData } from 'vue-chart-3';
import { Prop, Ref } from 'vue-property-decorator';
import { ChartData } from 'chart.js';
#Component({ components: { DoughnutChart } })
export default class DBOverviewDoughnut extends Vue {
#Prop()
private sharesData!: ChartData<'doughnut'>;
#Ref()
private readonly doughnutRef!: ExtractComponentData<typeof DoughnutChart>;
created(): void {
this.assignColors();
}
mounted(): void {
if (this.doughnutRef.chartInstance) {
console.log(this.doughnutRef.chartInstance.data);
}
}
assignColors(): void {
this.sharesData.datasets[0].backgroundColor = [
'#77CEFF',
'#0079AF',
'#123E6B',
'#97B0C4',
'#A5C8ED',
];
}
}
</script>
Starting the program it will work fine and I can access the chartInstance inside the mounted hook.
But now I want to unit test my component. I thought on setting the propsData which will be the input data for the chart.
DBOverviewDoughnut.spec.ts
import DBOverviewDoughnut from '#/components/dashboard/DBOverviewDoughnut.vue';
import { mount, Wrapper } from '#vue/test-utils';
import { Share } from '#/Share';
describe('DBOverviewDoughnut', () => {
let cut: Wrapper<DBOverviewDoughnut>;
it('should render the correct amount of sections', () => {
cut = mount(DBOverviewDoughnut, {
propsData: {
sharesData: {
labels: ['TestShare1', 'TestShare2', 'TestShare3'],
datasets: [{ data: [11, 22, 33] }]
}
}
});
const chart = cut.findComponent({ ref: 'doughnutRef' });
console.log(chart);
});
});
Using shallowMount() doesn't seem to work, because I only get this from logging (no chartInstance and its properties as in the production code):
VueWrapper {
isFunctionalComponent: undefined,
_emitted: [Object: null prototype] {},
_emittedByOrder: [],
selector: { ref: 'doughnutRef' }
}
So I thought maybe I have to mount the component because the DoughnutChart is also a wrapper around the Chart.js charts. But when using mount() I get the following error:
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: `createElement()` has been called outside of render function.
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: Error in render: "Error: [vue-composition-api] must call Vue.use(VueCompositionAPI) before using any function."
found in
---> <DoughnutChart>
<DBOverviewDoughnut>
<Root>
I don't really know what I'm doing wrong. I registered the VueCompostionAPI in my main.ts:
import Vue from 'vue';
import { Chart, registerables } from 'chart.js';
import App from './App.vue';
import router from './router';
import store from './store';
import VueCompositionAPI from '#vue/composition-api';
Chart.register(...registerables);
Vue.use(VueCompositionAPI);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
Following this post doesn't solve the problem either.
Anyone got an idea what's going wrong? I'm a bit confused if the error has to do with my test setup or with the installation of chart.js or compositionApi.
You need to use VueCompositionAPI inside your spec as well when you mount the component. You can do this by creating a local Vue instance inside your spec, adding VueCompositionAPI as a plugin to the instance and using the instance when you mount the component. https://vue-test-utils.vuejs.org/api/options.html#localvue
Using localVue is really what I should have thought about. This and installing the canvas-package works, that I get additional information about my Ref-Element. However I still have to figure out what to do with it.
#AdriHM I want to test if the rendered chat gets the correct data I guess. Or if it displays it correctly (e.g. display the correct amount of sections) But the longer I think about it the less I'm sure it's the right thing to test. I don't want to test the Chart.js API though.

Conditionally import a component in Vue Router

I'd like to conditionnaly import a component in the vue router. Here is what I have for the moment:
children: [
{
path: ':option',
component: () => import('../components/Option1.vue'),
},
],
Depending on what :option is, I want to import a different component (Option1.vue, Option2.vue, etc.). I know I could put several children but i actually need the option variable in my parent component (I make tests if the route has an option).
How would it be possible to do that?
Thanks in advance :)
You can create a loader component containing a dynamic component instead of doing conditional routing. In the loader, you'll conditionally lazy load the option component based on the route param. Not only is this easier when routing, you also don't have to manually import anything, and only options that are used will be imported.
Step 1. Route to the option loader component
router
{
path: ':option',
component: () => import('../components/OptionLoader.vue'),
}
Step 2. In that option loader template, use a dynamic component which will be determined by a computed called optionComponent:
OptionLoader.vue
<template>
<component :is="optionComponent" />
</template>
Step 3. Create a computed that lazy loads the current option
OptionLoader.vue
export default {
computed: {
optionComponent() {
return () => import(`#/components/Option${this.$route.params.option}.vue`);
}
}
}
This will load the component called "Option5.vue", for example, when the option route param is 5. Now you have a lazy loaded option loader and didn't have to manually import each option.
Edit: OP has now indicated that he's using Vue 3.
Vue 3
For Vue 3, change the computed to use defineAsyncComponent:
OptionsLoader.vue
import { defineAsyncComponent } from "vue";
computed: {
optionComponent() {
return defineAsyncComponent(() =>
import(`#/components/Option${this.$route.params.option}.vue`)
);
}
}
Here is something that works in VueJS3:
<template>
<component :is="userComponent"/>
</template>
<script>
import { defineAsyncComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
export default {
computed: {
userComponent() {
const route = useRoute();
const router = useRouter();
const components = {
first: 'Option1',
second: 'Option2',
third: 'OtherOption',
fourth: 'DefaultOption',
};
if (components[route.params.option]) {
return defineAsyncComponent(() => import(`./options/${components[route.params.option]}.vue`));
}
router.push({ path: `/rubrique/${route.params.parent}`, replace: true });
return false;
},
},
};
</script>
Source: https://v3-migration.vuejs.org/breaking-changes/async-components.html
And it's possible to get an error message like this one for the line with "return":
Syntax Error: TypeError: Cannot read property 'range' of null
In that case, it means you probably want to migrate from babel-eslint to #babel/eslint-parser (source: https://babeljs.io/blog/2020/07/13/the-state-of-babel-eslint#the-present)

Vue: lazy loading from library (Bootstrap-Vue) inside a component

I am exploring the lazy-loading feature and I'm trying to use it for Bootstrap-Vue component, but it does not work.
If I import the b-card "normally", it gets renderred correctly:
import { BCard } from 'bootstrap-vue';
export default {
components: {
BCard
}
};
But when I'm attempting the 'lazy-load' syntax it does not work:
export default {
components: {
BCard: () => import('bootstrap-vue').BCard
}
};
The b-card component is not renderred, but no error is thrown and in Chrome's DOM inspection tools I can see that the placeholder <!----> is placed by Vue where the b-card component should be. I suspect that the library object that is loaded does not have the BCard property, but I don't know how else to access the library component with the 'lazy' syntax.
Is it possible to lazy-load a module from a library? How to do it?
When dynamically importing a module, you use the import keyword as a function and it returns a promise. So, to access the module component, you can use this syntax:
export default {
components: {
BCard: () => import('bootstrap-vue').then(module => module.BCard)
}
}

How to inject Vuetify into my custom vue plugin

I would like to create a Vue plugin with a function which programatically renders a Vue component. That component depends on Vuetify. Everything works fine if I use vanilla HTML/CSS in that component, but using Vuetify-related things in there (e.g. a ) does not work. I assume that I didn't inject vuetify itself into the component correctly.
In my custom component, I tried importing every Vuetify component separately, but without success. I also tried creating the component with the syntax: new Vue({vuetify}), but also without success.
import MyCustomComponent from '#/components/MyCustomComponent'
import vuetify from '#/plugins/vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
const CustomComponent= Vue.extend(MyCustomComponent)
Vue.use(vuetify)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}
The error message indicates, that vuetify (or at least some of it's properties) are not available in my component
[Vue warn]: Error in getter for watcher "isDark": "TypeError: Cannot read property 'dark' of undefined"
HINT/EDIT: I am using Vuetify 2.0. The way Vuetify is injected into the app changed a little bit. Here's the code of my vuetify plugin file:
import Vue from 'vue';
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css';
import de from 'vuetify/es5/locale/de';
Vue.use(Vuetify)
export default new Vuetify({
theme: {
themes: {
light: {
primary: '#3f51b5',
secondary: '#b0bec5',
accent: '#8c9eff',
error: '#b71c1c'
},
},
},
});
Not sure if you solved this issue, but I had the same problem where Vuetify in a plugin would not be initialized correctly.
Vuetify documentation states that you need to define a vuetify option when creating your vue instance:
new Vue({
vuetify,
}).$mount('#app')
Fortunately, custom Vue plugins has an options parameter that we can use.
Here is the code that consumes your plugin:
const options = {}; // add other options here! (vuex, router etc.)
Vue.use(YourCustomPlugin, options);
new Vue(options).$mount('#app');
And here is your plugin code:
import vuetify from "./src/plugins/vuetify";
export default {
install(Vue, options) { // options is undefined unless you pass the options param!
Vue.component('my-custom-component', MyCustomComponent);
Vue.use(Vuetify);
options.vuetify = vuetify;
}
};
The vuetify module is very simple:
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
const opts = {}
export default new Vuetify(opts);
The problem is that you actualy don't export plugin itself in '#/plugins/vuetify';
import MyCustomComponent from '#/components/MyCustomComponent'
import Vuetify from 'vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
Vue.use(Vuetify)
const CustomComponent= Vue.extend(MyCustomComponent)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}