I have a Vue application that leverages Vuetify. In this application I have a component called city-selector.vue which is setup like this:
<template>
<select-comp
:id="id"
:items="cityList"
:item-text="name"
:item-value="cityCode"
#input="onInput">
</select-comp>
</template>
<script>
import VSelect from '../vuetify/VSelect';
export default {
name: 'city-select-comp',
extends: VSelect,
props: {
id: {
type: String,
default: '',
},
cityList: {
type: Array,
default: () => { return [] }
},
},
methods: {
onInput() {
//Nothing special, just $emit'ing the event to the parent
},
},
}
</script>
Everything with this component works fine except that when I open my dev tools I get a bunch of console errors all saying this (or something similar to this):
Cannot read property '$refs' of undefined
How can I fix this sea of red?
This is due to a bad import that you do not need. Remove the import VSelect and the extends statements and your console errors will disappear, like this:
<script>
export default {
name: 'city-select-comp',
props: {
id: {
type: String,
default: '',
},
cityList: {
type: Array,
default: () => { return [] }
},
},
methods: {
onInput() {
//Nothing special, just $emit'ing the event to the parent
},
},
}
</script>
Related
I am trying to develop guided tour with shepherd: https://www.npmjs.com/package/vue-shepherd but I cannot get the element. So here is my component for guide tour:
<template>
<div></div>
</template>
<script>
import { useShepherd } from 'vue-shepherd';
export default {
props: {
element: {
required: true,
},
id: {
type: Number,
required: true,
},
title: {
type: String,
},
text: {
type: String,
required: true,
},
position: {
type: String,
required: true,
},
},
mounted() {
this.tour.start();
},
data() {
return {
tour: null,
};
},
methods: {
createTour() {
this.tour = useShepherd({
useModalOverlay: true,
});
this.tour.addStep({
title: this.title,
text: this.text,
attachTo: { element: this.element, on: this.position },
buttons: [
{
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back',
},
{
action() {
return this.next();
},
text: 'Next',
},
],
id: this.id,
});
this.tour.start();
},
},
created() {
this.createTour();
},
};
</script>
and here is my parent component:
<button ref="button">
Click
</button>
<guide :element="element" :title="'Tour'" :text="'Example'" :position="'bottom'" :id="1" />
and the mounted of the parent element:
mounted() {
this.element = this.$refs.button;
},
but the tour doesnt attach the the button element. it just appears in the middle of the page. Why do you think it is?
Looks like a usage problem in vue hooks. The child component's hooks fire before the parent component's hooks. Therefore, at the time of the creation of the tour, the element does not exist. Vue-shepherd does not use vue reactivity.
Use
mounted() {
this.$nextTick(() => {
this.createTour();
});
},
Codesanbox
But it's better to change the component structure. If you are using vue3 you can use my package
In this case it will look like this
<template>
<button v-tour-step:1="step1">
Click
</button>
</template>
<script>
import { defineComponent, inject, onMounted } from "vue";
export default defineComponent({
setup() {
const tour = inject("myTour");
onMounted(() => {
tour.start();
});
const step1 = {
/* your step options */
}
return {
step1,
};
}
});
</script>
I have button which toggle data passed to this ChartComponent. If hold any seconds after toggle and retoggle so its work but if we doing it a bit fast, it is cause
Cannot read properties of null (reading 'getContext')? error.
<template>
<canvas></canvas>
</template>
<script>
import { defineComponent } from 'vue'
import Chart from 'chart.js/auto'
export default defineComponent({
name: 'ChartComponent',
props: {
type: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
options: {
type: Object,
default: () => ({}),
},
},
data: () => ({
chart: null,
}),
watch: {
data: {
handler() {
this.chart.destroy()
this.renderChart()
},
deep: true,
},
},
mounted() {
this.renderChart()
},
methods: {
renderChart() {
this.chart = new Chart(this.$el, {
type: this.type,
data: this.data,
options: this.options,
})
},
},
})
</script>
You should unwrap/unproxy this.chart before accessing it by using Vue3 "toRaw" function:
import { toRaw } from "vue";
...
watch: {
data: {
handler() {
toRaw(this.chart).destroy()
this.renderChart()
},
I have a pretty simple mixin that doesn't seem to be working using nuxt. It's so basic I'm at my whits end trying to figure out the bug. My component is part of a fairly complicated form composition scheme, but the mixin is super simple.
script from my component simplified
import { VInput } from 'vuetify'
import FieldMask from '~/utils/FieldMask'
import CognitoField from '~/mixins/cognitoField'
export default {
name: 'CognitoBaseField',
extends: [VInput, CognitoField],
props: {
inlineLabels: { default: true },
noIndependantSubmission: Boolean,
groupEdit: false,
cognitoName: '',
displayValue: '',
fieldValue: '',
placeHolder: { defalut: 'enter text' },
label: '',
masker: { type: Function, default: FieldMask },
items: [],
selected: {},
itemText: '',
itemValue: '',
showForm: Boolean,
disableEnterKeySubmission: Boolean,
},
data() {
return {
value: '',
formActive: false,
}
},
methods: {
onCancelClick() {
this.value = ''
this.formActive = false
console.log('test mixin')
console.log(this.thisIsATest())
},
},
}
My mixin - verbatim
export default {
computed: {
testComp() {
return 'working'
},
},
methods: {
thisIsATest() {
return 'working'
},
},
}
The result here is I trigger onCancelClick on the component and the page crashes with
TypeError: this.thisIsATest is not a function
To really include a usable version of the component in this question I'd need to share a number of components so its probably not called for. Unless somebody can obviously see a glaring error, I guess the real question is how to troubleshoot this further?
This should do it
<script>
import CognitoField from '~/mixins/cognitoField'
export default {
name: 'CognitoBaseField',
mixins: [CognitoField],
}
</script>
Regarding the documentation, this pattern is no longer recommended. The usage of Composables is better on several aspects.
I'm passing an array of objects into a child component and I want to access this array into the created function of the child component.
When logging the entire this.$props object I'm getting all the data however when I'm logging the array I'm getting an empty proxy object.
Parent component
<script>
import SelectWithSearch from '#/components/formElements/SelectWithSearch.vue';
import AddressService from '#/services/AddressService';
export default {
data() {
return {
city: 312,
cityOptions: [],
};
},
components: { SelectWithSearch },
created() {
this.getCities();
},
methods: {
async getCities() {
const response = await AddressService.getCities();
this.cityOptions = response.data.cities.map((city) => {
return {
id: city.id,
display_name: `${city.sub_city} (${city.zip})`,
};
});
},
},
};
</script>
Child component (SelectWithSearch)
<script>
export default {
props: {
label: { type: String, required: false },
options: { type: Array, required: true },
},
created() {
console.log(this.$props);
console.log(this.$props.options);
console.log(this.options);
console.log(this.label);
},
Output
How can I access the options data in the created function?
I'm currently working on an very versatile dashboard to display various data.
For the frontend I'm using the latest nuxt and vue version.
My dashboard has many kinds of variations to display data (for example pie charts, line charts,...) these are described in components which are called dynamically.
The Problem is that when I browse from the Page "/" to another (for example "/foo") the interval gets fired again and crashes the app.
That happenes after the lifecycle hook destroyed. I tried to define the interval as an variable and stop it in the beforeDestroy hook but it did not help.
let interval= setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
/* later */
clearInterval(interval);
Do you see an error?
Thank you.
Thats the relevant code:
Template
<no-ssr>
<v-container grid-list-md>
<v-layout row wrap v-masonry transition-duration="0.5s" item-selector=".flex" column-width="#grid-sizer">
<v-flex xs1 sm1 md1 lg1 x1 id="grid-sizer"></v-flex>
<component :key="dashboardItem.id" v-for="(dashboardItem,index) in dashboardItems" :is="dashboardItem.type" :connector="dashboardItem.connector"
:type="dashboardItem.type" :configuration="dashboardItem.configuration" :id="dashboardItem.id" :index="index"></component>
</v-layout>
</v-container>
</no-ssr>
Script
import OnlyValue from '#/components/dashboardItems/OnlyValue.vue'
import TrafficLight from '#/components/dashboardItems/TrafficLight.vue'
import ChartAllHover from '#/components/dashboardItems/ChartAllHover.vue'
import PieChart from '#/components/dashboardItems/PieChart.vue'
import Section from '#/components/dashboardItems/Section.vue'
import Gauge from '#/components/dashboardItems/Gauge.vue'...
export default {
name: 'HomePage',
head () {
return {
title: "Dashboard"
}
},
computed: {
...mapGetters({
isAuthenticated: "users/isAuthentificated",
profileName: "profiles/name",
dashboardItems: "profiles/dashboardItems"
})
},
mounted() {
if (typeof this.$redrawVueMasonry === 'function') {
this.$redrawVueMasonry()
}
},
components: {
OnlyValue,
TrafficLight,
ChartAllHover,
PieChart,
Section,
Gauge
}
}
When calling a components it looks the following:
import dashboardItem from '~/mixins/dashboardItem'
export default {
name: "gauge",
mixins: [dashboardItem],
props: {
connector: {
type: String,
required: true
},
type: {
type: String,
required: true
},
configuration: {
type: Object,
required: true
},
id: {
type: Number,
required: true
},
index: {
type: Number,
required: true
}
},
data: () => ({
initOptions: {
renderer: 'svg'
},
options: {
tooltip: {
formatter: "{c}%"
},
series: [{
name: null,
type: 'gauge',
detail: {
formatter: '{value}%'
},
data: null
}]
},
isLoading: true
}),
methods: {
getData(configuration, connector) {
this.fetchData(configuration, connector)
setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
},
fetchData(configuration, connector) {
this.getSingleValue(configuration, connector)
.then(data => {
this.isLoading = false
let percent = (data.value / configuration.max) * 100
percent = Math.round(percent * 10) / 10
this.$nextTick(function () {
this.$refs.gauge.mergeOptions({
series: [{
name: data.title,
data: [{
value: percent,
name: data.title
}]
}]
})
})
this.$redrawVueMasonry()
})
.catch(e => console.log(e))
}
},
mounted () {
this.getData(this.configuration, this.connector)
}
}