Prop not Reactive using VUE to send configuration - vue.js

I have started learning Vue and came quite far :) but still consider myself beginner. I have bought a vue based single page application template
Themeforest Template Link
I am building my application over it and it uses FlatPickr
I am trying to use FlatPickr's enable functionality
{
enable: ["2025-03-30", "2025-05-21", "2025-06-08", new Date(2025, 8, 9) ]
}
I asked the developer how to use this as it was not in instructions so he replied:
you can use the config prop to pass all Flatpickr options.
I have codes which looks like this
<c-flatpickr
v-validate="'required'"
v-model="dateRange"
:config="enabledDates"
name="Select Date Range"
value="" range
/>
enabledDates: function (){
var data = this.trip.tripsDetails;
var options = {
enable: [],
};
for (let i in data){
options.enable.push(data[i].startingDate);
}
return options;
},
my problem is if I pass the data statically it works so if I put values in array manually like
var options = {
enable: ['01-06-2018', '03-06-2018', '06-06-2018'],
};
it works but if I try to push values dynamically (as per my codes above) it doesn't work.
What am I doing wrong?
Thanks for your help in advance.
// Here are the codes
data() {
return {
trip: new Form({
//All Trips
tripsDetails: [],
}),
dateRange: undefined,
}
},
methods: {
getData(pageid){
this.trip.tripsDetails = [];
axios
.get('/trip/getTrips',{
params: {
equipment_id : this.trip.equipment_id,
dispatch_id: this.pageid
}
})
.then(response => {
this.trip.tripsDetails = response.data.data;
this.loading();
}).catch(error => {
this.loading();
this.errortoast();
});
},

Related

How Do I Resolve this "An error was captured in current module: TypeError: e.parse is not a function"

How do I solve this Vue Js error on Shopware 6 Administration. The module is suppose to select a column in the database table.
PS. This is the complete code. I'm trying to read data from the database and view it in the twig template.
const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
import template from './store-settings-page.html.twig'
Component.register('store-settings-page', {
template,
inject: [
'repositoryFactory'
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data: function () {
return {
entity: undefined,
storeData: null,
entityId: '4e2891496c4e4587a3a7efe587fc8c80',
secret_key: 'hdkkjjsmk538dncbjmns',
public_key: '1destinoDet2123lefmoddfk##$$%O',
}
},
computed: {
storeKeysRepository() {
return this.repositoryFactory.create('store_keys');
},
},
created() {
this.storeKeysRepository
.get(this.entityId, Shopware.Context.api)
.then(entity => {
this.entity = entity;
});
console.log(entity);
},
});
Apologies if my knowledge of Vue & JS is a bit off, based on how I see Shopware codes it, I recommend data to be written like this:
data() {
return {
...
};
}
I would also try to strip your file to the bear minimum to see when the error disappears.
Another thing to check is if you are running a JS file or TS file. Maybe it's having a hard time parsing your file because you are extending store-settings-page and it assumes it should be TypeScript?
this.storeKeysRepository
.get(this.entityId, Shopware.Context.api)
.then(entity => {
this.entity = entity;
console.log(this.entity);
});
This will do the trick

How to generate computed props on the fly while accessing the Vue instance?

I was wondering if there is a way of creating computed props programatically, while still accessing the instance to achieve dynamic values
Something like that (this being undefined below)
<script>
export default {
computed: {
...createDynamicPropsWithTheContext(this), // helper function that returns an object
}
}
</script>
On this question, there is a solution given by Linus: https://forum.vuejs.org/t/generating-computed-properties-on-the-fly/14833/4 looking like
computed: {
...mapPropsModels(['cool', 'but', 'static'])
}
This works fine but the main issue is that it's fully static. Is there a way to access the Vue instance to reach upon props for example?
More context
For testing purposes, my helper function is as simple as
export const createDynamicPropsWithTheContext = (listToConvert) => {
return listToConvert?.reduce((acc, curr) => {
acc[curr] = curr
return acc
}, {})
}
What I actually wish to pass down to this helper function (via this) are props that are matching a specific prefix aka starting with any of those is|can|has|show (I'm using a regex), that I do have access via this.$options.props in a classic parent/child state transfer.
The final idea of my question is mainly to avoid manually writing all the props manually like ...createDynamicPropsWithTheContext(['canSubmit', 'showModal', 'isClosed']) but have them populated programatically (this pattern will be required in a lot of components).
The props are passed like this
<my-component can-submit="false" show-modal="true" />
PS: it's can-submit and not :can-submit on purpose (while still being hacked into a falsy result right now!).
It's for the ease of use for the end user that will not need to remember to prefix with :, yeah I know...a lot of difficulty just for a semi-colon that could follow Vue's conventions.
You could use the setup() hook, which receives props as its first argument. Pass the props argument to createDynamicPropsWithTheContext, and spread the result in setup()'s return (like you had done previously in the computed option):
import { createDynamicPropsWithTheContext } from './props-utils'
export default {
⋮
setup(props) {
return {
...createDynamicPropsWithTheContext(props),
}
}
}
demo
If the whole thing is for avoiding using a :, then you might want to consider using a simple object (or array of objects) as data source. You could just iterate over a list and bind the data to the components generated. In this scenario the only : used are in the objects
const comps = [{
"can-submit": false,
"show-modal": true,
"something-else": false,
},
{
"can-submit": true,
"show-modal": true,
"something-else": false,
},
{
"can-submit": false,
"show-modal": true,
"something-else": true,
},
]
const CustomComponent = {
setup(props, { attrs }) {
return {
attrs
}
},
template: `
<div
v-bind="attrs"
>{{ attrs }}</div>
`
}
const vm = Vue.createApp({
setup() {
return {
comps
}
},
template: `
<custom-component
v-for="(item, i) in comps"
v-bind="item"
></custom-component>
`
})
vm.component('CustomComponent', CustomComponent)
vm.mount('#app')
<script src="https://unpkg.com/vue#3"></script>
<div id="app">{{ message }}</div>
Thanks to Vue's Discord Cathrine and skirtle folks, I achieved to get it working!
Here is the thread and here is the SFC example that helped me, especially this code
created () {
const magicIsShown = computed(() => this.isShown === true || this.isShown === 'true')
Object.defineProperty(this, 'magicIsShown', {
get () {
return magicIsShown.value
}
})
}
Using Object.defineProperty(this... is helping keeping the whole state reactive and the computed(() => can reference some other prop (which I am looking at in my case).
Using a JS object could be doable but I have to have it done from the template (it's a lower barrier to entry).
Still, here is the solution I came up with as a global mixin imported in every component.
// helper functions
const proceedIfStringlean = (propName) => /^(is|can|has|show)+.*/.test(propName)
const stringleanCase = (string) => 'stringlean' + string[0].toUpperCase() + string.slice(1)
const computeStringlean = (value) => {
if (typeof value == 'string') {
return value == 'true'
}
return value
}
// the actual mixin
const generateStringleans = {
created() {
for (const [key, _value] of Object.entries(this.$props)) {
if (proceedIfStringlean(key)) {
const stringleanComputed = computed(() => this[key])
Object.defineProperty(this, stringleanCase(key), {
get() {
return computeStringlean(stringleanComputed.value)
},
// do not write any `set()` here because this is just an overlay
})
}
}
},
}
This will scan every .vue component, get the passed props and if those are prefixed with either is|can|has|show, will create a duplicated counter-part with a prefix of stringlean + pass the initial prop into a method (computeStringlean in my case).
Works great, there is no devtools support as expected since we're wiring it directly in vanilla JS.

How to push to an array via a Mutation in Vuex?

At the time I was writing this question I found the solution to my problem, but even so I decided to share it with the community to see if I'm solving the problem in the best way possible.
Given a summary of my Store:
// store/index.js
const store = createStore({
state: {
userBooks: [],
}
mutations: {
setUserBooks(state, val) {
state.userBooks.push(val);
},
actions: {
addBook({ commit }, payload) {
commit("setUserBooks", payload);
}
})
I'm calling the action like this:
// Add.vue
methods: {
addBook(book) {
this.$store.dispatch("addBook", book);
},
}
This was giving me the following error:
Uncaught (in promise) TypeError: state.userBooks.push is not a function
books is an object obtained through a v-for and contains properties like id, title, author, thumbnail, and ISBN.
I had already checked this solution: Push to vuex store array not working in VueJS. And that's exactly what I tried, but I got the above error.
How I solved the problem:
I noticed that the book object was coming into the function as a proxy object. With that in mind, I turned the proxy object into a regular object as follows:
addBook(book) {
book = Object.assign({}, book);
this.$store.dispatch("addBook", book);
}
Why does the problem happen?
I confess that I still don't understand why the problem occurs. book is obtained via the v-for of books.
books is assembled from a Google Books API query. The query is done using axios.get().then()
The console.log(this.books) already returns me a proxy object and I confess that I don't know if this is the expected behavior and if I should try to change it.
Anyway the problem is solved, but if anyone has any different approach I would be very happy to learn something new.
EDIT: More code
I decided to edit the question to show how books are generated and populated.
<template>
<figure v-for="(book, index) in books" :key="index">
<Button text="+" #click="addBook(book)" />
<!-- I omitted the use of the other elements to make things more objective. -->
</figure>
</template>
<script>
export default {
data() {
return {
books: {},
};
},
methods: {
search() {
axios
.get(`https://www.googleapis.com/books/v1/volumes?q=${this.seek}`)
.then((response) => {
this.books = response.data.items.map((item) => ({
id: item.id,
title: item.volumeInfo.title,
authors: item.volumeInfo.authors || [this.$t("book.unknown-author")],
ISBN: item.volumeInfo.industryIdentifiers?.[0].identifier ?? item.id,
thumbnail: item.volumeInfo.imageLinks?.thumbnail ?? this.noCover,
}));
})
.catch((error) => console.error(error))
},
addBook(book) {
// Object.assign({}, book)
book = { ...book };
this.$store.dispatch("addBook", book);
},
},
};
</script>
Another new faster way is spread operator. You create a new object and spred variables inside book object. It works same as book = Object.assign({}, book)
book = { ...book }
Bellow more examples of usage of spred operator:
You can use it in arrays to if val is an array with and object if not just don't type ... before val.
setUserBooks(state, val) {
state.userBooks = [...state.userBooks, ...val];
}
Or for example you have big object called user and you have in this object his address object and he wants to change it.
setUser(state, address) {
state.user = {...state.user, address};
}

How do I use API to fetch data for chart in the context of Vuejs

I'm new to Vuejs, and I want to make my code effective by fetching the data from API rather than giving the data directly.
Here is my code:
<template>
<canvas id="myChart" width="550" height="300"></canvas>
</template>
<script>
export default {
name: 'Chart',
data: () => ({
arrdate: [
1600934100.0,
1602009600.0,
1602747060.0,
1603050158.390939,
1603305573.992575
],
arrchallenge: [
9.0,
9.5,
2.5,
11.52,
12.4
]
}),
mounted () {
// eslint-disable-next-line no-unused-vars
const data = this.arrdate.map((arrdate, index) => ({
x: new Date(arrdate * 1000),
y: this.arrchallenge[index]
}))
const ctx = document.getElementById('myChart').getContext('2d')
// eslint-disable-next-line no-undef,no-unused-vars
const myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
data,
label: 'Performance',
borderColor: '#7367F0'
}
]
},
options: {
scales: {
xAxes: [
{
type: 'time',
time: {
unit: 'month',
displayFormats: {
month: 'MMM YYYY'
}
}
}
],
yAxes: [
{
ticks: {
// eslint-disable-next-line no-unused-vars
callback (value, index, values) {
return `${value }%`
}
}
}
]
}
}
})
}
}
</script>
As you can see, the "date" and "challenge" contains data which is fed directly, but I want to fetch data from API.
What my API returns:
{
"date": [
1600934100.0,
1602009600.0,
1602747060.0,
1603050158.390939,
1603305573.992575
],
"challenge": [
9.1
9.5
-2.8
18.52
15.4
]
}
So as you can see my API, I want the "date's" data to be in arrdate and "challenge's" data to be in arrchallenge by using API.
Someone please help me with this, and if someone knows the answer please send me the changes by adding it to my code itself because I'm new to vuejs wrapping the contents would be difficult for me.
first add axios to your project and read the link below from vue documentation:
using axios with vue
after that you have two options:
Call API on page load
with this option your API calls as soon as your vue app loads so in your created hook do the API call like this:
created() {
axios.get('yourAPIUrl').then((response) => {
// reassign date and challenge with the response from the API call here for example:
this.date = response.data.date;
this.challenge = response.data.challenge
});
}
basically what it does is that you call the API when your vue is created and in the then part you use the response to updata the variables defined in your data object.
Call API on button click
with this method you have a button on your page like this:
<button #click="callAPI">get data from server</button>
when you click the button it calls the callAPI method and in your methods you have the same code as before, like this:
methods: {
callAPI() {
// same code as in the created example
}
}
you can also use async ... await syntax for API call if you want to.
also you can read this article on how to install and use axios in your project:
use axios API with vue CLI
I created this API for you to use to test out the solutions provided by anyone:
https://wirespec.dev/Wirespec/projects/apis/Stackoverflow/apis/fetchChartDataForVueJs
And here is the response:
https://api.wirespec.dev/wirespec/stackoverflow/fetchchartdataforvuejs
You can also create your own API on Wirespec and use it to generate more data (sequential or random) if you need more diverse testing.

Accessing data in created from props Vue.js

I am not able to access data from props in created function.
Its working fine in methods as you can see in below code.
And its working in methods as you see below
export default {
props: ['projectId'],
data() {
return {
elements: []
}
},
created() {
axios.get(`api/projects/${this.projectId}/elements`).then(response => {
this.elements = response.data.data
});
},
methods: {
addingElement(element) {
alert(this.projectId);
}
}
}
Parent template
<add-project-element :projectId="project.id"></add-project-element>
Thanks
<add-project-element :projectId="project.id"></add-project-element> has to be
<add-project-element :project-id="project.id"></add-project-element>
HTML attribute names are case-insensitive. Any uppercase character will be interpreted as lowercase. So camelCased prop names need to use their kebab-cased equivalents.
There is a typo in your axios request. Your prop is projectId but you have project.id there...
Ok so finally I found something, I am not sure if its right but its working fine.
actually, props are rendering after created that's why its shows undefine. so I set some time. and it worked
created() {
setTimeout(() => {
axios.get(`/api/projects/${self.projectId}/elements`)
.then(response => {
this.elements = response.data.data
});
}, 1)
},
Thanks everyone