Sending Vuex State To Backend - vue.js

I am trying to send a state in Vuex that is an array of objects to my backend. However, I keep getting this error:
Uncaught (in promise) TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property '$options' -> object with constructor 'Object'
| property 'router' -> object with constructor 'Object'
--- property 'app' closes the circle
at JSON.stringify (<anonymous>)
at transformRequest (defaults.js?6d1b:52)
at transform (transformData.js?e0e9:16)
at Object.forEach (utils.js?a2f8:232)
at transformData (transformData.js?e0e9:15)
at dispatchRequest (dispatchRequest.js?82e5:30)
I am using axios to post to my backend, and here is my axios post method call:
SET_ITEMS: (state) => {
const items = state.items;
axios.post('http://localhost:5000/api/boards/update-list', { list: items });
}
I have tried multiple things one being the uses flatted package (Flatted), however, to no avail. How can I fix this error? Thanks in advance.
EDIT
In my case, I have manually given value to the state.items.
state: {
items: [
{
x: 100,
y: 200,
},
{
x: 50,
y: 400,
},
{
x: 100,
y: 100,
},
{
x: 350,
y: 300,
}
]
}
SECOND EDIT
Moved async call into a action:
setItems: ({state}) => {
axios.post('http://localhost:5000/api/boards/update-list', { hey: state.items})
}

I didn't mention this in the question, but I have a value in state.items where the element (HTMLElement) is being stored. This has been causing the error mentioned in the question. How I solved this was by completing leaving out the element value from JSON.stringify. You can accomplish this by creating a replacer function and here is mine in my case:
function replacer(key, value) {
if (key === 'element') return undefined;
return value;
}
Then you can put this function in the JSON.stringify as shown here:
JSON.stringify(state.items, replacer(key, value))
And now the element value is not included after the JSON.stringify method call!
I hope someone finds this helpful.

Related

Call SQL linter's API from Codemirror with Typescript

I am trying to call an API to lint a SQL query written in Codemirror (actually I use Angular and the wrapper ngx-codemirror)
Unfortunately, I could not call the API because this is considered undefined:
data-analyzer.component.html:81 ERROR TypeError: Cannot read property 'analyzerService' of undefined
at testA (data-analyzer.component.ts:624)
at lintAsync (lint.js:134)
at startLinting (lint.js:152)
at Object.lint (lint.js:248)
at new CodeMirror (codemirror.js:7885)
at CodeMirror (codemirror.js:7831)
at Function.fromTextArea (codemirror.js:9682)
at ctrl-ngx-codemirror.js:64
at ZoneDelegate.invoke (zone-evergreen.js:365)
at Zone.run (zone-evergreen.js:124)
My code is as follow:
<ngx-codemirror
#ref
name="query"
[options]="config"
[(ngModel)]="item.query"
(keypress)="CMonKeyPress($event)"
>
</ngx-codemirror>
config = {
mode: 'text/x-mysql',
showHint: true,
lint: {
lintOnChange: true,
getAnnotations: this.lintSQL
},
gutters: [
'CodeMirror-linenumbers',
'CodeMirror-lint-markers'
]
};
constructor(
private analyzerService: DataAnalyzerService
) {}
lintSQL(a: string, b: LintStateOptions, cm: Editor) {
const found: Annotation[] = [];
// The error occurs here
this.analyzerService.lint(this.item.query).subscribe(
(r: any) => {
console.log(r.data);
},
(err) => {
console.log(err);
}
);
// So far I return an empty array, the focus is to get the results from the service
return found;
}
I would like to know how could I access to the service in the linting function.
Found from this question, the solution is to bind (this) as follow:
getAnnotations: this.lintSQL.bind(this)

How to use state in data function in vue

Having problem to use state in data function of Vue.
I tried this
this.items = this.$store.state.search_items
but it always results in an empty array like this
[__ob__: Observer]
length: 0
__ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
__proto__: Array
Any help would be appreciated. Thank you !!
Here is the mutation from which I am stating search_items
this is working because I am able to see state.search_items in Vue dev tool
SET_WORKSPACES(state, payload) {
state.workspaces = payload;
var items = [];
if (state.workspaces.length) {
state.workspaces.forEach(function(workspace) {
// Adding workspaces
if (workspace.portfolios.length) {
items = [...items, ...workspace.portfolios];
}
// Adding projects
if (workspace.projects.length) {
items = [...items, ...workspace.projects];
// Adding tasks
workspace.projects.forEach(function(project) {
if (project.tasks.length) {
items = [...items, ...project.tasks];
}
});
}
});
}
state.search_items = items;
}
Data property
data() {
return {
results: [],
keys: ['title','description'],
list:this.items,
}
},
If the state is updating correctly, the time taking for setting the state is making the issue. That means, the the function you're using to assign the state to data is rendering before the state set.
You can use a computed function that returning the state
computed:{
items : function(){
return this.$store.state.search_items;
}
}
Now you can use the computed function name items same like a data variable.

Error in callback for watcher “get_settings”: “TypeError: Cannot read property ‘general’ of undefined”

Please help me out, how to handle this error i cant seem to handle this out as i am new to vue.
what im doing is getting data from server in store vuex with action. Now in component im accessing that data with getter in computed property and trying to watch that property but on component mount i get that error in console but functionality works fine.
data: function() {
return {
settings_flags :{
general: 0,
privacy: 0,
layouts: 0,
message: 0
}
}
}
1: mounting
mounted() {
let self = this;
self.userid = this.getUserId();
this.$store.dispatch('getGallerySettings',self.req);
self.initial_settings();
}
2: computed
computed: {
get_settings() {
return this.$store.getters.getGallerySettings;
}
}
3: watch
watch: {
'get_settings': {
deep: true,
handler() {
let self =this;
if (this.$_.isMatch(self.get_settings.gallery_settings.general,self.initialSettings.gallery_settings.general) == false) {
self.settings_flags.general = 1;
} else {
self.settings_flags.general = 0;
}
}
}
}
It seems to me that your watcher is looking for a property 'general' that is a child of gallery_settings.
get_settings.gallery_settings.general
In the meantime in data you have a property general that is a child of 'settings_flags'. Those two don't line up. So make sure that either your watcher is looking for something that exists when the component starts up, or tell your watcher to only start watching ' get_settings.gallery_settings.general' when 'get_settings.gallery_settings' actually exists.
if (get_settings.gallery_settings) { do something } #pseudocode
I'm not sure that's your problem, but it might be.

Vue.js 2: action upon state variable change

I am using a simple state manager (NOT vuex) as detailed in the official docs. Simplified, it looks like this:
export const stateholder = {
state: {
teams: [{id: 1, name:'Dallas Cowboys'}, {id: 2, name:'Chicago Bears'}, {id: 3, name:'Philadelphia Eagles'}, {id:4, name:'L.A. Rams'}],
selectedTeam: 2,
players: []
}
getPlayerList: async function() {
await axios.get(`http://www.someapi.com/api/teams/${selectedTeam}/players`)
.then((response) => {
this.state.players = response.data;
})
}
}
How can I (reactively, not via the onChange event of an HTML element) ensure players gets updated (via getPlayerList) every time the selectedTeam changes?
Any examples of simple state that goes a little further than the official docs? Thank you.
Internally, Vue uses Object.defineProperty to convert properties to getter/setter pairs to make them reactive. This is mentioned in the docs at https://v2.vuejs.org/v2/guide/reactivity.html#How-Changes-Are-Tracked:
When you pass a plain JavaScript object to a Vue instance as its data
option, Vue will walk through all of its properties and convert them
to getter/setters using Object.defineProperty.
You can see how this is set up in the Vue source code here: https://github.com/vuejs/vue/blob/79cabadeace0e01fb63aa9f220f41193c0ca93af/src/core/observer/index.js#L134.
You could do the same to trigger getPlayerList when selectedTeam changes:
function defineReactive(obj, key) {
let val = obj[key]
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
return val;
},
set: function reactiveSetter(newVal) {
val = newVal;
stateholder.getPlayerList();
}
})
}
defineReactive(stateholder.state, 'selectedTeam');
Or you could set it up implicitly using an internal property:
const stateholder = {
state: {
teams: [/* ... */],
_selectedTeam: 2,
get selectedTeam() {
return this._selectedTeam;
},
set selectedTeam(val) {
this._selectedTeam = val;
stateholder.getPlayerList();
},
players: []
},
getPlayerList: async function() {
/* ... */
},
};
Your question is also similar to Call a function when a property gets set on an object, and you may find some more information there.
You could use v-on:change or #change for short to trigger getPlayerList.
Here a fiddle, simulating the request with setTimeout.

Accessing a method from another method and passing value in Vue

I got this.
Now, I wan't to know if this is possible
computed : {
X: function(a,b){
return(a*b)
},
Y: function(e){
var c = this.X(3,2)
return(c)
}
}
I want to be able to send two arguments (3,2) into function:X and then have the computed result sent back to Y. So far, this has failed. I've read this but it led me nowhere in particular.
Use methods rather than computed:
methods: {
X: function(a,b){
return(a*b)
},
Y: function(e){
var c = this.X(3,2)
return(c)
}
}