How to update a property in Inertia.js and Vue.js? - javascript

I am trying to update a property list in Vue.js using Inertia.js:
props: {
list: {
type: Object,
default: {}
}
},
updateTable(filters) {
axios.post(route('updateList'), filters)
.then(r => {
this.list = r.data
})
}
But I get the following error: TypeError: 'set' on proxy: trap returned falsish for property
In Inertia.js, all props are provided as proxies. As described in this mdn article, the proxie's set method needs to return true to allow the assignment. However, I have no idea how to correctly achieve that as I do not create the proxy myself. Any suggestions?
Or is it that with Inertia I would always be forced to use a partial reload?

I believe there's some misunderstandings of Vue itself and how Inertia works here.
You're receiving the list as a prop. Props should not be directly changed.
If you ABSOLUTELY need to change it, you can reference it directly by this.$page.props.list instead of receiving the list as a prop.
Or, you can do this:
export default {
props: {
list: {
type: Object,
default: {}
}
},
data () {
return {
listCopy: this.list
}
},
mounted () {
// Handle your scroll events
axios.get(this.listCopy.next_page_url).then(response => {
this.listCopy = {
...response.data,
data: [...this.listCopy.data, ...response.data.data]
}
})
}
}

Related

VueJS: Is it possible to use spread operator for computed properties?

I just wonder if I could do the code below less ugly.
In the component I have a property person. I'd like to use fields of the person object in my template without prepending each field with person.something. But the only way I know is below.
This is what I have atm:
(Please consider the code below as just an example, it's not a real one)
{
name: 'Demo',
props: {
person: {
type: Object,
default: {}
}
},
computed: {
firstName() {
return this.person.firstName
},
lastName() {
return this.person.lastName
},
age() {
return this.person.age
},
gender() {
return this.person.gender
}
}
}
This is what I want to have instead (kind of):
{
name: 'Demo',
props: {
person: {
type: Object,
default: {}
}
},
computed: {
...this.person // <-- something like this would be better if only it would work
}
}
Some assumptions
I assume that things like this should be possible, because we have mapGetters of vuex:
computed: {
...mapGetters({ something: SOMETHING })
},
With vue 3 or the composition api plugin for vue 2 you could use toRefs to spread the prop value inside the setup hook :
import {toRefs} from 'vue'//vue3
//import {toRefs} from '#vue/composition-api'//vue 2
export default{
name: 'Demo',
props: {
person: {
type: Object,
default: {}
}
},
setup(props){
return {...toRefs(props.person)}
}
}
I see your point but what you want is not possible.
The main problem is this. We work with Options API. What is computed? An object that is passed into a Vue and Vue creates new instance with computed property for each function (or get/set pair) inside computed object. That means the spread operator is executed at the time component instance does not exist yet which means there is no this
mapGetters works because it's input are just static strings. If you had some static description of the Person object - for example some schema generated from Open API specification - you could create mapProperties helper and use it to generate computed props...
Edit:
Yes, there is a way to create computed props dynamically in beforeCreate by modifying $options object - at least it was possible in Vue 2. Not sure about Vue 3. In both cases it is documented to be read only and Vue 3 is somehow more strict in forcing "read onlyness". However this is very different approach from the one in your question...
The approach is demonstrated for example here

Vue FireStore Reactive Key Assignment - Object is possibly 'undefined'

I am attempting to assign FireStore data(), forwarded by props, to a reactive() proxy object, but am receiving the following error:
Object is possibly 'undefined'.
(property) fireStoreData: Record<string, any> | undefined
I wish to use a forEach loop instead of direct assignments i.e. (pageForm.name = props.fireStoreData?.name) to minimize code.
props: {
fireStoreData: Object,
}
...
setup(props){
...
const pageForm: { [key: string]: any } = reactive({
name: '',
address: '',
...
})
onMounted(() => {
Object.keys(pageForm).forEach(key => {
if(key in props.fireStoreData) // <-- error found here
pageForm[key] = props.fireStoreData[key] // <-- and here
})
})
})
...
}
The issie is that the fireStoreData prop is not required yet in your code you assume it is.
try:
props: {
fireStoreData: {
required: true,
type: Object
}
}
If you dont want the prop to be required, its always possible to check for it being defined and add a watcher to look for changes.
Also, dont forget props can change in value.
// on mobile, will format later on pc

Use ES6 classes as vuex store state in Nuxt

When I set ES6 class to state on my vuex store in nuxt I got following warn:
WARN Cannot stringify arbitrary non-POJOs EndPoint
and When I use object as state is works without warning.
So How can I use ES6 classes in my state.
My model:
export default class EndPoint {
constructor(newEndPoints) {
this.login = newEndPoints.login;
this.status = newEndPoints.status;
}
}
and mutate state here:
commit(CoreMutations.SET_ENDPOINTS, new EndPoint(response.data));
Using object:
const EndPoint = {
endPoints(newEndPoints) {
return {
login: newEndPoints.login,
status: newEndPoints.status
};
}
};
and mutate:
commit(CoreMutations.SET_ENDPOINTS, EndPoint.endPoints(response.data));
As discussion in comment add toJSON method in class solve the problem when setting state in client, but when set state in server, states will become undefined.
so adding this code solve the problem:
toJSON() {
return Object.getOwnPropertyNames(this).reduce((a, b) => {
a[b] = this[b];
return a;
}, {});
}

Vue Local Data Using Getter

I'm trying to set a data property (in Classroom) based on what's in the store (of Lesson). But I keep getting undefined. How can I get that value and set it in data?
Classroom.vue:
data(){
return {
selectedOption: this.currentLesson
}
}
computed: Object.assign({},
mapGetters({
currentLesson: "lesson/current"
})
)
lesson.js:
const state = {
current: {}
}
const getters = {
current(){
return state.current
},
}
index.js:
export default new Vuex.Store({
modules: {
lesson,
user
}
})
UPDATE:
The component's data function is called before the computed values are set up. So you cannot use computed properties inside data function. (That is reasonable, because some computed getters might rely on certain properties from data. It might cause infinite loops if we set up computed values before calling data).
In your case, if you want selectedOption to always be the same as currentLesson, then you don't need to put it in the component's local data, just use this.currentLesson directly in your view template.
However, if you just want to set up an initial value for selectedOption based on lesson/current, you can explicitly access it via:
selectedOption: this.$store.getters['lesson/current']
Or by using a lifecycle hook like created or mounted:
created () {
this.selectedOption = this.$store.getters['lesson/current']
// or `this.selectedOption = this.currentLesson` if you keep that mapped getter
}
Original answer:
currentLesson: "lesson/current" is the "namespaced getter" syntax. You need to set namespaced: true for your lesson store definition. Did you set that field when you export lesson.js?

Saving Vuex State on mounted lifecycle

I'm trying to save the application's root state on mounted lifecycle of VueJS and freeze the copy in $root's data, and my attempt is as following,
mounted() {
this.globalState = this.$store.state;
},
data () {
return {
globalState: null
}
}
However this approach is updating globalState so I came up with another way to freeze it by using Object.freeze() but with no luck it keeps updating.
Also I've tried to copy the this.$store.state to a const variable and update globalState via it, but it also fails.
My last attempt is as following, I know it's an ugly code but please bear with it.
let emptyObj = {};
Object.defineProperty(emptyObj, 'globalState',{
value: this.$store.state,
enumerable: false,
configurable: false,
writable: false
}
);
this.globalState = emptyObj.globalState;
My question is, how can I copy and freeze the initial state of the application and store it in single data?
Since you do not want reactivity on the copy of the state object its better to create a custom option in your components option instead of freezing the object in the data option.
import {cloneDeep} from "lodash"
export default{
myGlobalState: null,
mounted() {
this.$options.myGlobalState = cloneDeep(this.$store.state);
}
}
Your custom option can be accesed using vm.$options and is not reactive
this.$options.myGlobalState
You can do that by using a computed value which has an empty setter.
e.g.
import _ from 'lodash';
{
data() { return {} },
computed: {
globalState: {
get() {
return _.cloneDeep(this.$store.state);
},
set(input) {}
}
},
mounted(){
this.globalState = {test2:456};
// this won't change anything
}
}
https://codepen.io/jacobgoh101/pen/bvgMRR?editors=1111

Categories