I would like to have a dynamic breadcrumbs based on where I clicked on a category but I get an error that says my variable is undefined: TypeError: Cannot read properties of undefined (reading 'homeMenu'). Yet in my getHomeCategory function, the console.log of homeCategory displays Perma'Thèque. I don't understand how to do it, thanks
Here is the code :
<script>
export default {
props: {
},
data: () => ({
homeMenu: "",
breadcrumbs: [
{
text: 'Accueil',
disabled: false,
href: '/',
},
{
text: this.homeMenu,
disabled: false,
href: "/" + this.homeMenu,
},
],
}),
computed: {
...mapGetters({
console: () => console,
homeCategory: 'home/getCategory',
})
},
methods: {
getHomeCategory () {
if (this.homeCategory === "Perma'Thèque") {
console.log(this.homeCategory)
return this.homeMenu = "permatheque"
} else {
return this.homeMenu = "null"
}
},
},
mounted() {
if (this.plantActive) this.loading = false;
this.getHomeCategory()
}
}
</script>
data() is declared here as an arrow function, so this refers to the outer scope, not the Vue component instance, but even as a regular function here, this.homeMenu won't yet exist.
It seems that you actually want breadcrumbs to be reactive to homeMenu, so you should move breadcrumbs to a computed prop:
export default {
data: () => ({
homeMenu: '',
}),
computed: {
breadcrumbs() {
return [
{
text: 'Accueil',
disabled: false,
href: '/',
},
{
text: this.homeMenu,
disabled: false,
href: '/' + this.homeMenu,
},
]
}
}
}
demo
I used Element Plus UI and my mind was blown. Check out my blog and the codes. Element Plus is a free library that is great for Vue JS. They have very nice UI for breadcrumb and can be implemented with v-for loop.
https://medium.com/#samchowdhury/create-a-breadcrumb-with-vue-js-and-element-plus-ui-f3e2fde50a3e
Related
I just need some help identifying what I am missing here. Just can't seem to send the correct data through:
Parent with the CommunicationPreference component:
<CommunicationPreference
v-for="(communication, index) in communicationPreference"
:key="index"
:consent="communication.consent"
:name="communication.name"
#update="updateConsent(consent)"
/>
METHOD
methods: {
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
},
},
CommunicationPrefernce.vue
<Button
class="mr-4"
:text="YES"
:type="consent === true ? 'primary' : 'secondary'"
#clicked="updateConsent(true)"
/>
<Button
:text="NO"
:type="consent !== true ? 'primary' : 'secondary'"
#clicked="updateConsent(false)"
/>
PROPS:
props: {
type: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
consent: {
type: Boolean,
default: true,
},
},
METHOD:
updateConsent(consent) {
this.$emit('update', consent)
},
STORE:
async updateCommunicationPreferences({ commit, state }, payload) {
const { consent } = payload
const { communicationTypeName } = state.communicationTypeName
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`, consent)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},
Attached is the UI I am working towards for reference. the idea is each time the user selects either YES or NO the selection is updated and reflected on the UI
Here is my Swagger doc:
I assume that you have a mapped getter for communicationPreference prop, so that this is correct.
I also assume that your #clicked event prop is proper provided the implementation of Button.vue.
So try to change #update="updateConsent(consent)" to #update="updateConsent"
Right now it seems to me that you are making a small mistake between a function call and declaration. Having it such as #update="updateConsent" will trigger updateConsent method, and the function declaration:
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
},
will take care of getting the consent you pass in your event trigger.
I am trying to write a simple test for my vue component. Since the vue component makes an async call on mount and updates the vuex store, dispatch is called during mount, which breaks my existing unit tests. Any idea how to overcome this? Since I am mocking table data, I don't need the mounted() function to be called when running the tests.
MyTable.spec.js
const wrapper = shallowMount(MyTable, {
propsData: {
tableData: [
{
"product_id":10826345236,
"name":"T-Shirt"
}
],
columns: ['product_id', 'name'],
headings: ['Product ID', 'Name'],
actionType: 'loadProducts'
}
});
...
MyTable.vue
...
data() {
return {
options: {
...
}
};
},
methods: {
getHeadings() {
let headings = {};
this.columns.map((key, i) => headings[key] = this.headings[i]);
return headings;
},
setColumnClasses() {
let classes = {};
this.columns.map((key) => classes[key] = key);
return classes;
},
loadRecords(actionType) {
this.$store.dispatch(actionType);
}
},
props: {
tableData: {
type: Array,
required: true
},
columns: {
type: Array,
required: true
},
actionType: {
type: String,
required: true
},
headings: {
type: Array,
required: true
},
...
},
mounted() {
this.loadRecords(this.actionType);
}
You are getting this error message because Vue (when mounted) is expecting that the this.$store is defined, and while it might be within your application, you are not importing it, nor are you mocking it.
Here is your test function code you provided:
const wrapper = shallowMount(MyTable, {
propsData: {
tableData: [
{
"product_id":10826345236,
"name":"T-Shirt"
}
],
columns: ['product_id', 'name'],
headings: ['Product ID', 'Name'],
actionType: 'loadProducts'
}
});
Here is what you need to add:
import store from '../path/to/store.js';
import { createLocalVue, shallowMount } from '#vue/test-utils';
// You will want to create a local Vue instance for testing purposes: https://vue-test-utils.vuejs.org/api/#createlocalvue
const localVue = createLocalVue();
// This tells the local Vue instance to use Vuex for the store mechanism.
localVue.use(Vuex);
const wrapper = shallowMount(MyTable, {
localVue, // Bind the local Vue instance when we shallow-mount the component.
store, // Bind the store so all of the methods are available when invoked.
propsData: {
tableData: [
{
"product_id":10826345236,
"name":"T-Shirt"
}
],
columns: ['product_id', 'name'],
headings: ['Product ID', 'Name'],
actionType: 'loadProducts'
}
});
In my project, I want to add some Ajax loaded menu items to my CoreUI sidebar in Vue. I already found a working solution, but it's kind of hacky and might have timing issues. Therefore I want to ask you, if there is a proper or at least better solution.
I also found this question from a few days ago, but it doesn't have an answer yet.
// main.js
new Vue({
el: '#app',
router,
icons,
template: '<App/>',
components: {
App
},
data: {
clientConfiguration: null
},
created: async function () {
let svcResult = await this.$http.get('Picking/ViewerSettings');
this.clientConfiguration = svcResult.data;
this.$children[0].$children[0].$children[0].$data.nav[0]._children[0].items =
svcResult.data.map(vc => ({
name: vc.name,
to: 'test/' + vc.name,
icon: 'cil-spreadsheet'
}));
}
})
// _nav.js
export default [
{
_name: 'CSidebarNav',
_children: [
{
_name: 'CSidebarNavDropdown',
name: 'Lists',
to: '/test',
icon: 'cil-list-numbered',
items: []
},
// ...
]
}
]
The _nav.js file is just an example of data structure that can be rendered by CRenderFunction component docs
The idea behind CRenderFunction is that you can render components from the Array/Object.
In your case, you have two options:
generate CRenderFunction object on backend,
generate CRenderFunction object on frontend by computed properties, based on data you got from the backend
Here is the example of the second approach:
in template
<CRenderFunction flat :content-to-render="navItems"/>
in script:
//example array that you receive from backend
const menuItems = [
{
name: 'first item',
to: '/first',
icon: 'cil-user'
},
{
name: 'second item',
to: '/second'
},
{
name: 'third item',
to: '/third'
}
]
export default {
computed: {
navItems () {
return [
{
_name: 'CSidebarNav',
_children: this.sidebarNavChildren
}
]
},
sidebarNavChildren () {
return menuItems.map(menuItem => {
return {
_name: 'CSidebarNavItem',
name: menuItem.name,
to: menuItem.to,
icon: menuItem.icon || 'cil-spreadsheet'
}
})
}
}
}
navItems computed property result:
[{"_name":"CSidebarNav","_children": [
{"_name":"CSidebarNavItem","name":"first item","to":"/first","icon":"cil-user"},
{"_name":"CSidebarNavItem","name":"second item","to":"/second","icon":"cil-spreadsheet"},
{"_name":"CSidebarNavItem","name":"third item","to":"/third","icon":"cil-spreadsheet"}
]
}]
I am using tabulator with vue. Packages: tabulator-tables and vue-tabulator.
I'm trying to set a click event on the tabulator row, but need to access the vue data variables within the click event.
The script looks like this:
import axios from "axios";
import qs from "qs";
import { TabulatorComponent } from "vue-tabulator";
export default {
components: {
name: "Main",
CustomerData: TabulatorComponent
},
data() {
return {
tab: "",
customer_data: "",
customers: null,
cdata: [
{
f_name: "",
l_name: ""
}
],
customer_options: {
rowClick: function(e, row) {
axios
.post(
"php/getcustomer.php",
qs.stringify({
id: row.getCell("id").getValue()
})
)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
},
layout: "fitColumns",
columns: [
{
title: "ID",
field: "id",
sorter: "string",
visible: false,
},
{
title: "First",
field: "f_name",
sorter: "string",
editor: false,
},
{
title: "Last",
field: "l_name",
sorter: "string",
editor: false,
}
]
}
};
},
methods: {},
created() {
axios.get("php/getcustomers.php").then(response => {
this.cdata = JSON.parse(JSON.stringify(response)).data;
});
}
};
The how do I access the vue variable "customer_data" from within the "rowClick" event handler?
Your rowClick is not a method. You need to move it under the methods key and then you can access this.customer_data.
// ...Rest of the code
customer_options: {
rowClick: this.rowClick
},
// Rest of the data
methods:{
rowClick(e, row){
// Do your thing with this.customer_data
}
}
Also, why are you defining a function under customer_options. It seems weird.
Solution posted by Ankit works - by defining your handler inside methods handler has access to this which represents your Vue component instance (this is not defined for handler defined as anonymous function as in your question).
Other option is to use native Vue event vue-tabulator provides. Like this:
<TabulatorComponent v-model="cdata" :options="customer_options" #row-click="onRowClick"/>
methods: {
onRowClick(e, row) {
console.log(
`Clicked on row ${row.getCell("id").getValue()} (${this.tab})`
);
}
}
Case:
I am trying dynamically mount functional vue component inside custom directive
(I need to show special button, when user hovers on some elements on the page). I do it in this way, dynamically, because I have thousands of elements on the page, so I try to avoid excess components and try to mount them on the fly, while hovering.
Directive code:
import AIcon from './ui/AIcon';
let AIconComponent = Vue.extend(AIcon);
editable: {
bind: (element, binding, vNode) => {
element.addEventListener('mouseover', () => {
element.style.position = 'relative';
let editButtonWrp = htmlToElement(`<div class="btn-wrp"><span class="btn"></span></div>`);
element.prepend(editButtonWrp);
new AIconComponent({
el: editButtonWrp.querySelector('.btn'),
parent: vNode.context,
props: {
size: 'xs', icon: 'edit',
},
});
});
}
}
Button functional component code:
export default {
functional: true,
props: {
icon: {
type: String,
default: '',
},
size: {
type: String,
default: 'md',
}
},
render(createElement, { props }) {
let iconHref = `/dist/img/sprite.svg#${props.icon}`;
return createElement('svg', {
class: { 'a-icon': true, [`-${props.icon}`]: true },
}, [
createElement('use', {
attrs: {
'xlink:href': iconHref,
},
}),
]);
},
}
But on the page I get this error:
TypeError: Cannot read property 'props' of undefined
at Proxy.render (AIcon.js?27d5:19)
As I understand, when I call new AIconComponent({ ... }) in directive, it passes undefind context to render(createElement, { props }) ... but why? How I can resolve this case?