Get whole state object in pinia action - vue.js

How can I get whole state obejct(only the state "values" without actions) inside pinia action? "this" return whole store object.
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: "apple",
color: "green",
}),
actions: {
myaction() {
const wholeState = state?; // {count: 0, name: "apple", color: "green"}
console.log(wholestate)'
},
},
})

You can use this.$state.
Document

Related

Update object property state inside of watch vue

I'm trying to update the selected boolean to true from my lists items array of objects when the from_id property from the items array inside of the entry object matches with the item_id of the object in lists fromData array. The selected property updates but I got this error:
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers.
store.js
entry: {
items: [
{
from_id: 5,
to_id: 1,
quantity: 100,
},
{
from_id: 11,
to_id: 3,
quantity: 119,
},
{
from_id: 7,
to_id: 3,
quantity: 59,
},
]
}
lists: {
from: [
{
item: {...},
item_id: 5,
selected: false
},
{
item: {...},
item_id: 6,
selected: false,
},
{
item: {...},
item_id: 7,
selected: false,
}
]
}
const getters = {
entry: (state) => state.entry,
lists: (state) => state.lists,
};
This what I've tried
computed: {
...mapGetters('Conversion', ['entry', 'lists']),
from: function () {
return this.lists.from.filter((item) => !item.selected);
},
},
watch: {
from: {
deep: true,
handler: function (items) {
const _entries = this.entry.items;
_entries.map((entry) => {
items
.filter((item) => item.item_id === entry.item_from_id)
.map((item) => (item.selected = !item.selected));
});
},
},
},
The error means what it says. You're mutating the state outside the store. This should happen in mutation/action.
You need to have an action/mutation that modifies the state this way:
mutations: {
toggle(state, id) {
let item = state.items.find(item => item.id === id)
item.selected = !item.selected;
}
}
And dispatch/commit it outside the store:
this.$store.dispatch('toggle', someId)
Vuex is one-way data flow store. You will not able to mutate data directly with the getters, use action/mutation instead. https://vuex.vuejs.org/#what-is-a-state-management-pattern
In your store.js
const store = createStore({
...
mutations: {
patchEntry (state, payload) {
// Your logic here => return _entries
state.entry = Object.assign({}, state.entry, _entries)
}
}
})
In your watcher:
watch: {
from: {
deep: true,
handler: function (items) {
store.commit('patchEntry', items)
});
},
},
},
Ref: https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components

Apexchats.js axios gives me undefined with Vue

I am trying to get data from server using vue and apexcharts, but even after I called data with axios, it gives me undefined..
What have I missed?
template
<apexchart
ref="chart1"
width="100%"
:options="chartOptions" :series="series">
</apexchart>
data from url
{
"pageviews": 1313,
"new_users": 1014
}
script
export default {
data: function () {
return {
series: [],
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
},
created: function () {
this.getByVisitor()
},
methods: {
getByVisitor() {
const url = 'url';
axios
.get(url)
.then(response => {
this.$refs.chart1.updateSeries([{
name: 'Sales',
data: response.data
}])
})
.catch(error => (this.byVisitor = error.data));
console.log(`---------------this.$refs.chart1`, this.$refs.chart1);
},
}
See Updating Vue Chart Data
There's no need to directly call the updateSeries() method on the chart component since it is able to react to changes in series. All you have to do is update your series data property
export default {
data: () => ({
series: [], // 👈 start with an empty array here
byVisitor: null, // 👈 you seem to have missed this one for your error data
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
}),
created: function() {
this.getByVisitor()
},
methods: {
async getByVisitor() {
const url = 'url';
try {
const { data } = await axios.get(url)
// now update "series"
this.series = [{
name: "Sales",
data
}]
} catch (error) {
this.byVisitor = error.data
}
},
}
}

Get tiptap-vuetify to work with "Mentions" in a Vue Nuxt app

so my problem is that I can't seem to make use of the "Mentions" functionality of tiptap inside a vuetify-nuxt project.
The original example can be found here
More useful info:
Example implementation from the tiptap github here
Similar question asked and not answered fully here
Similar question asked in the vuetify integration library here
To do that I'm trying to combine documentation examples.
I guess it goes wrong around the time I try to use "tippy", which is the css library used to display the popup that actually lists users to pick from (after the #), but I can't seem to understand the real issue.
So when I type # the keydown/up event listeners are functioning, but the tippy seems to not bind the popup successfully (it's not displayed), and the following error occurs:
Editor.vue?6cd8:204 Uncaught TypeError: Cannot read property '0' of undefined
at VueComponent.enterHandler (Editor.vue?6cd8:204)
at onKeyDown (Editor.vue?6cd8:175)
at Plugin.handleKeyDown (extensions.esm.js?f23d:788)
at eval (index.es.js?f904:3298)
at EditorView.someProp (index.es.js?f904:4766)
at editHandlers.keydown (index.es.js?f904:3298)
This is my tippy.js nuxt plugin:
import Vue from "vue";
import VueTippy, { TippyComponent } from "vue-tippy";
Vue.use(VueTippy, {
interactive: true,
theme: "light",
animateFill: false,
arrow: true,
arrowType: "round",
placement: "bottom",
trigger: "click",
// appendTo: () => document.getElementById("app")
});
Vue.component("tippy", TippyComponent);
This is the component in which I'm trying to show the editor and the suggestions/mentiosn functionality:
<template>
<div>
<div class="popup">
aaaa
</div>
<editor-menu-bar v-slot="{ commands }" :editor="editor">
<div class="menubar">
<v-btn class="menubar__button" #click="commands.mention({ id: 1, label: 'Fred Kühn' })">
<v-icon left>#</v-icon>
<span>Mention</span>
</v-btn>
</div>
</editor-menu-bar>
<tiptap-vuetify v-model="localValue" :extensions="extensions" :native-extensions="nativeExtensions" :toolbar-attributes="{ color: 'grey' }" #init="onInit" />
</div>
</template>
<script>
// import the component and the necessary extensions
import {
TiptapVuetify,
Heading,
Bold,
Italic,
Strike,
Underline,
Code,
CodeBlock,
Image,
Paragraph,
BulletList,
OrderedList,
ListItem,
Link,
Blockquote,
HardBreak,
HorizontalRule,
History,
} from "tiptap-vuetify";
// TESTING
import { EditorMenuBar, Editor } from "tiptap";
import { Mention } from "tiptap-extensions";
import tippy, { sticky } from "tippy.js";
export default {
components: { TiptapVuetify, EditorMenuBar },
props: {
value: {
type: String,
default: "",
},
},
data: () => ({
editor: null,
extensions: null,
nativeExtensions: null,
// TESTING
query: null,
suggestionRange: null,
filteredUsers: [],
navigatedUserIndex: 0,
insertMention: () => {},
popup: null,
}),
computed: {
localValue: {
get() {
return this.value;
},
set(value) {
this.$emit("input", value);
},
},
// TESTING
hasResults() {
return this.filteredUsers.length;
},
showSuggestions() {
return this.query || this.hasResults;
},
},
created() {
this.extensions = [
History,
Blockquote,
Link,
Underline,
Strike,
Italic,
ListItem,
BulletList,
OrderedList,
[
Heading,
{
options: {
levels: [1, 2, 3],
},
},
],
Bold,
Link,
Code,
CodeBlock,
Image,
HorizontalRule,
Paragraph,
HardBreak,
];
this.nativeExtensions = [
// https://github.com/ueberdosis/tiptap/blob/main/examples/Components/Routes/Suggestions/index.vue
new Mention({
// a list of all suggested items
items: async () => {
await new Promise((resolve) => {
setTimeout(resolve, 500);
});
return [
{ id: 1, name: "Sven Adlung" },
{ id: 2, name: "Patrick Baber" },
{ id: 3, name: "Nick Hirche" },
{ id: 4, name: "Philip Isik" },
{ id: 5, name: "Timo Isik" },
{ id: 6, name: "Philipp Kühn" },
{ id: 7, name: "Hans Pagel" },
{ id: 8, name: "Sebastian Schrama" },
];
},
// When # is pressed, we enter here
onEnter: ({ items, query, range, command, virtualNode }) => {
this.query = query; // the field that the # queries? currently empty
this.filteredUsers = items;
this.suggestionRange = range;
this.renderPopup(virtualNode); // render popup - failing
this.insertMention = command; // this is saved to be able to call it from within the popup
},
// probably when value after # is changed
onChange: ({ items, query, range, virtualNode }) => {
this.query = query;
this.filteredUsers = items;
this.suggestionRange = range;
this.navigatedUserIndex = 0;
this.renderPopup(virtualNode);
},
// mention canceled
onExit: () => {
// reset all saved values
this.query = null;
this.filteredUsers = [];
this.suggestionRange = null;
this.navigatedUserIndex = 0;
this.destroyPopup();
},
// any key down during mention typing
onKeyDown: ({ event }) => {
if (event.key === "ArrowUp") {
this.upHandler();
return true;
}
if (event.key === "ArrowDown") {
this.downHandler();
return true;
}
if (event.key === "Enter") {
this.enterHandler();
return true;
}
return false;
},
// there may be built-in filtering, not sure
onFilter: async (items, query) => {
await console.log("on filter");
},
}),
];
},
methods: {
// TESTING
// navigate to the previous item
// if it's the first item, navigate to the last one
upHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + this.filteredUsers.length - 1) %
this.filteredUsers.length;
},
// navigate to the next item
// if it's the last item, navigate to the first one
downHandler() {
this.navigatedUserIndex =
(this.navigatedUserIndex + 1) % this.filteredUsers.length;
},
enterHandler() {
const user = this.filteredUsers[this.navigatedUserIndex];
if (user) {
this.selectUser(user);
}
},
// we have to replace our suggestion text with a mention
// so it's important to pass also the position of your suggestion text
selectUser(user) {
this.insertMention({
range: this.suggestionRange,
attrs: {
id: user.id,
label: user.name,
},
});
this.editor.focus();
},
renderPopup(node) {
if (this.popup) {
return;
}
// ref: https://atomiks.github.io/tippyjs/v6/all-props/
this.popup = tippy(".page", {
getReferenceClientRect: node.getBoundingClientRect, // input location
appendTo: () => document.body, // must be issue
interactive: true,
sticky: true, // make sure position of tippy is updated when content changes
plugins: [sticky],
content: this.$refs.suggestions,
trigger: "mouseenter", // manual
showOnCreate: true,
theme: "dark",
placement: "top-start",
inertia: true,
duration: [400, 200],
});
},
destroyPopup() {
if (this.popup) {
this.popup[0].destroy();
this.popup = null;
}
},
beforeDestroy() {
this.destroyPopup();
},
/**
* NOTE: destructure the editor!
*/
onInit({ editor }) {
this.editor = editor;
},
},
};
</script>
How can I get the "suggestions" item display in the aforementioned setting?

VueJS + Chartjs - Chart only renders after code change

I'm using the following tutorial (pull download stats for NPM packages) to build a basis for my charted webapp :
https://hackernoon.com/lets-build-a-web-app-with-vue-chart-js-and-an-api-544eb81c4b44
https://github.com/apertureless/npm-stats
I have extracted the below code from the tutorial and modified it so it does the pure basics. Get data and present data. Specifically from these:
https://github.com/apertureless/npm-stats/blob/develop/src/pages/Start.vue
https://github.com/apertureless/npm-stats/blob/develop/src/components/LineChart.vue
Please note: The code executes the API call and retrieves data no problem. However it will only render that data in the chart if I make a code change. For example changing the color of a line to something else. It seems to only work on the next 'cycle' if that makes sense. Once the data has rendered, if I refresh that page it is once again blank. I suspect it has something to do with the pages timing. However not sure where to begin or what I'm looking for.
App.Vue
<template>
<v-app style="background-color: rgb(228, 228, 228);">
<section class="One">
<v-card class="One" color="rgb(255, 255, 255)" >
<LineChart :chart-data="downloads" :chart-labels="labels"/>
</v-card>
</section>
</v-app>
</template>
<script>
import axios from 'axios';
import LineChart from './components/test3.vue';
export default {
name: 'App',
components: {
LineChart,
},
data () {
return {
package: '',
packageName: '',
loaded: false,
loading: false,
downloads: [],
downloadsYear: [],
downloadsMonth: [],
downloadsWeek: [],
labels: [],
labelsYear: [],
labelsMonth: [],
labelsWeek: [],
showError: false,
showSettings: false,
errorMessage: 'Please enter a package name',
periodStart: '',
periodEnd: new Date(),
rawData: '',
totalDownloads: '',
dailyPng: null,
weeklyPng: null,
monthlyPng: null,
yearlyPng: null
}
},
mounted(){
this.loaded = false
axios.get(`https://api.npmjs.org/downloads/range/2017-01-01:2017-04-19/vue`)
.then(response => {
this.rawData = response.data.downloads
this.downloads = response.data.downloads.map(entry => entry.downloads)
this.labels = response.data.downloads.map(entry => entry.day)
this.packageName = response.data.package
this.totalDownloads = this.downloads.reduce((total, download) => total + download)
this.setURL()
this.groupDataByDate()
this.loaded = true
this.loading = false
})
.catch(err => {
this.errorMessage = err.response.data.error
this.loading = false
})
},
};
</script>
Chart Component:
<script>
import { Line } from 'vue-chartjs'
export default {
extends: Line,
props: {
chartData: {
type: Array,
required: false
},
chartLabels: {
type: Array,
required: true
}
},
data () {
return {
gradient: null,
options: {
showScale: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: false,
},
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}],
xAxes: [ {
gridLines: {
display: true,
color: '#EEF0F4',
borderDash: [5, 15]
}
}]
},
tooltips: {
backgroundColor: '#4F5565',
titleFontStyle: 'normal',
titleFontSize: 18,
bodyFontFamily: "'Proxima Nova', sans-serif",
cornerRadius: 3,
bodyFontColor: '#20C4C8',
bodyFontSize: 14,
xPadding: 14,
yPadding: 14,
displayColors: false,
mode: 'index',
intersect: false,
callbacks: {
title: tooltipItem => {
return `🗓 ${tooltipItem[0].xLabel}`
},
label: (tooltipItem, data) => {
let dataset = data.datasets[tooltipItem.datasetIndex]
let currentValue = dataset.data[tooltipItem.index]
return `📦 ${currentValue.toLocaleString()}`
}
}
},
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false
}
}
},
mounted () {
this.gradient = this.$refs.canvas
.getContext('2d')
.createLinearGradient(0, 0, 0, 450)
this.gradient.addColorStop(0, 'rgba(52, 217, 221, 0.6)')
this.gradient.addColorStop(0.5, 'rgba(52, 217, 221, 0.25)')
this.gradient.addColorStop(1, 'rgba(52, 217, 221, 0)')
this.renderChart({
labels: this.chartLabels,
datasets: [
{
label: 'downloads',
borderColor: '#249EBF',
pointBackgroundColor: 'rgba(0,0,0,0)',
pointBorderColor: 'rgba(0,0,0,0)',
pointHoverBorderColor: '#249EBF',
pointHoverBackgroundColor: '#fff',
pointHoverRadius: 4,
pointHitRadius: 10,
pointHoverBorderWidth: 1,
borderWidth: 1,
backgroundColor: this.gradient,
data: this.chartData
}
]
}, this.options)
setTimeout(() => {
this.download()
}, 500)
},
methods: {
formatNumber (num) {
let numString = Math.round(num).toString()
let numberFormatMapping = [[6, 'm'], [3, 'k']]
for (let [numberOfDigits, replacement] of numberFormatMapping) {
if (numString.length > numberOfDigits) {
let decimal = ''
if (numString[numString.length - numberOfDigits] !== '0') {
decimal = '.' + numString[numString.length - numberOfDigits]
}
numString = numString.substr(0, numString.length - numberOfDigits) + decimal + replacement
break
}
}
return numString
}
}
}
</script>
You need to notify the child component to re-render itself.
add a watcher is one way, watch the data change and update it.
Another easier way is, add a key prop to it.
in your App.vue, do like this:
<LineChart :chart-data="downloads" :chart-labels="labels" :key="downloads.length"/>
here i'm using the downloads's length as key value. it's a simple and temp resolution to show you how to use key. In your app you should use some other value as key, incase different api call returns same length data.
you can also set the key to another value, and change this value every time you call the api.

Unable to populate data array for VueJS Calendar plugin with dynamic Vuex store data

I have a Calendar Component in a VueJS app generated by the vue-sweet-calendar plugin. The component renders properly using the static data, but when I comment that data out (as seen in the code below) and try to fill the array with real data, the array generated is empty.
Here is the component:
<template>
<div id="calendar-card">
<!-- Using Component -->
<calendar
:eventCategories="eventCategories"
:events="events"
ref="calendar"
/>
</div>
</template>
<script>
// Importing Component and style
import { Calendar } from 'vue-sweet-calendar'
import 'vue-sweet-calendar/dist/SweetCalendar.css'
import ActsService from '#/services/ActsService'
export default {
name: 'CalendarCard',
data() {
return {
eventCategories: [
{
id: 1,
title: 'Personal',
textColor: 'white',
backgroundColor: 'Blue'
},
{
id: 2,
title: 'Company-wide',
textColor: 'white',
backgroundColor: 'red'
}
],
events: [
// SAMPLE Calendar data commented out
// {
// title: 'Event 1',
// start: '2019-10-02',
// end: '2019-10-02',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 2',
// start: '2019-10-08',
// end: '2019-10-08',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 3',
// start: '2019-10-10',
// end: '2019-10-10',
// repeat: 'never',
// categoryId: 2
// },
// {
// title: 'Event 4',
// start: '2019-10-23',
// end: '2019-10-23',
// repeat: 'never',
// categoryId: 2
// }
]
}
},
computed: {
userActs() {
return this.$store.getters.userActs
},
},
mounted() {
this.$store.dispatch("getUserActs");
this.setEvents();
},
methods: {
goToday() {
this.$refs.calendar.goToday()
},
setEvents() {
console.log('setting events using these userActs: ', this.$store.state.userActs)
this.$store.state.userActs.forEach(function(item) {
events.push({
item: {
title: item.deed,
start: item.created_at,
end: item.created_at,
repeat: 'never',
categoryId: 2
}
})
})
console.log('these are the calendar events', this.events);
}
},
components: {
Calendar // Registering Component
}
}
</script>
In the Chrome Vue console, I can see state.userActs populated with 4 Act objects, however, console.log(this.$store.state.userActs) returns an array with 0 elements.
In the Chrome Vue console, I can see state.userActs populated with 4 Act objects, however, console.log(this.$store.state.userActs) returns an array with 0 elements.
This sounds like your Vuex store is populated / updated after your component is mounted.
I would suggest creating a computed property for events so that it automatically updates when new store data appears.
For example
computed: {
userActs() {
return this.$store.getters.userActs
},
events() {
return this.userActs.map(item => ({
title: item.deed,
start: item.created_at,
end: item.created_at,
repeat: 'never',
categoryId: 2
}))
}
}
Also remove events from your data. You will no longer need a setEvents() method either.