my component:
<div id="event-picker">
<template v-for="event in $store.state.events">
{{ event.artist }}
</template>
</div>
my store (mutations):
prepareEventForm(state, event) {
state.form.time = event.time
state.form.date = event.date
state.form.event = event.event
state.form.artist = event.artist
state.form.organizer = event.organizer
state.form.location = event.location
state.showForm = true
}
The error I get is Cannot read property 'time' of undefined
Where could be the problem?
EDIT:
this is my action method:
prepareEventForm({ commit }) {
commit('prepareEventForm')
}
The reason you are getting that error is that the event object being passed to your prepareEventForm mutation is undefined.
This is because when you call $store.dispatch('prepareEventForm', event), it calls your prepareEventForm action, passing event in as the second parameter.
You need to add event as the second parameter of your action and pass that as the second parameter in your commit call (which is what calls the prepareEventForm mutation):
prepareEventForm({ commit }, event) {
commit('prepareEventForm', event)
}
Related
How i can load data after click on vue-select, if i do this it's load aftet two click
<v2-select style="width:150px;max-width: 150px"
#click="loadFormats"
:searchable="false"
:clearable="false
"
:options="formats"
v-model="localSelectedFormat.value"
label="nameFormat"
>
</v2-select>
loadFormats(){
this.getFormats().then(res => {
this.formats = res.data;
...
})
you can remove the click event and call the loadFormats function in the mounted lifecycle hook function.
code below:
<v2-select style="width:150px;max-width: 150px"
:searchable="false"
:clearable="false"
:options="formats"
v-model="localSelectedFormat.value"
label="nameFormat"
>
</v2-select>
mounted(){
this.loadFormats()
}
```
i've a reactive object, on the save function i call toRaw in order to remove de object reactivity, but, when i change the reactive object props, also the group object is changing....
How???
const newGroup = reactive<Group>({ id: undefined, name: '', enabled: false })
const handleSave = () => {
const group = toRaw(newGroup)
persist(group).then(() => {
newGroup.id = undefined
newGroup.name = 'demo'
console.log(isReactive(group)) //say false but the group.name value is demo
})
}
destructuring the reactive obj as const group = { ...newGroup } looks like it works, still no understand why toRaw is not working.
EDIT:
the whole problem comes from a sense of lack of control when dealing with reactive objects for example:
the following cumputed object retrieves a list from the store which is then drawn in the template as rows of a table at which click the selected element is changed of state. in the function I am forced to deconstruct records to avoid that the modification trigger the change of enabled before it is persisted
Is there a better way?
Should I make readonly groups?
//in setup()
const groups = computed <Group []> (() => getters ['registry / groups'])
const toggle = (record: Group) => {
const group = { ...record }
group.enabled = !group.enabled
persist(group)
}
//in template
<a-menu-item # click = "toggle (record)">
This is expected behaviour, but the description may not be detailed enough and cause some confusion.
according to toRaw docs
Returns the raw, original object of a reactive or readonly proxy. This is an escape hatch that can be used to temporarily read without incurring proxy access/tracking overhead or write without triggering changes. It is not recommended to hold a persistent reference to the original object. Use with caution.
What is happening when you use toRaw, is that you're getting back the original object that you passed into the reactive function without the Proxy. That means that the object is the same object (o and g in example 👇). toRaw only allows you to "escape" the reactivity/listeners bound to the object if you use toRaw instead of the Proxy itself. Even though the objects update, the reactivity (trickling down to DOM) is not applied.
Here is an example:
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g = Vue.toRaw(m);
console.log(o.name, m.name, g.name);
g.name = g.name + " Ding Dong";
console.log(o.name, m.name, g.name);
console.log("Is 'm' same as 'o'? ", m == g);
console.log("Is 'g' really just 'o'? ", o == g);
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
If you want to create an object that will not update the original object, the simplest way is to clone it.
Most of the time you can use JSON.parse(JSON.stringify(o)). The caveat is that you get into issues if you have cyclical references (ie. {a:b, b:a}).
Another option is to use destructuring const copy = {...o}. The caveat here is that it is a shallow copy. Example o = {a:{b:"I will update"}, c:"I won't"}, where copy.a is still same object as o.a
That would then look like either
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g0 = {...Vue.toRaw(m)}; // destructure
let g1 = JSON.parse(JSON.stringify(Vue.toRaw(m))); // clone with json
Alternatively, you can also use some library that handles cloning object for you.
You have a misunderstanding of reactivity here. A reactive object connects the data with DOM, not between javascript objects, i.e. when a property of the reactive object is updated, the DOM is automatically updated.
toRaw returns the raw, original object of a reactive or readonly proxy, i.e group and newGroup are still the same object, the difference is that group would not trigger a DOM update as newGroup does.
Example, UI is updated when you update newGroup, but updating group won't trigger a DOM change. However the two objects still have the same reference, which is why group.name and newGroup.name are always the same.
Vue.createApp({
setup() {
const newGroup = Vue.reactive({ id: undefined, name: '', enabled: false })
const group = Vue.toRaw(newGroup)
return {
newGroup,
group
}
}
}).mount('#app')
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<div id="app">
<button #click="newGroup.name = 'reactive update'">reactive update</button>
newGroup Name: {{newGroup.name}}<br/>
<button #click="group.name = 'raw update'">raw update</button>
group Name: {{group.name}}
</div>
it is normal that isReactive(group) return false.
Actually in your code we have const group = toRaw(newGroup)
=> group is the original object of the reactive value newGroup.
=> group is a raw value, not a reactive value
if you tried isReactive(newGroup) this will return true
My solution is based the article in:
https://chrysanthos.xyz/article/how-to-get-the-data-of-a-proxy-object-in-vue-js-3/
1 - First import { toRaw } from "vue";
2 - In my vuex on return getters:
`
// getters
const getters = {
listOperations:(state: { operations: any; }) => {
return toRaw(state.operations);
}
}
`
3 - Browser: (9) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
I have an input which is bidden to property message. I have a set button , which I use for changing of input value programmatically. When the button is pressed, value of input is correctly changed to 'xxxx'. when I press clean button after that , the input is correctly cleaned but when I repeat pressing set and clean again the input does not get cleared anymore.
Working example is here:
https://codepen.io/dan-ouek/pen/rNBjxRO
<div class="container">
<div id='root' class="box">
<input ref="templateFilter" type='text' id='input' v-model='message'>
{{ message }}
<button #click="set">set</button>
<button #click="clean">clean</button>
</div>
</div>
new Vue({
el: '#root',
data: {
message: 'Hello Vue'
},
methods: {
set: function () {
let element = this.$refs.templateFilter
Vue.set(element, 'value', 'xxxx')
}, clean: function () {
this.message = ""
}
}})
Questions:
1) Is it possible to change input programmatically without working with bidden property value? Something like direct DOM manipulation:
let element = document.getElementsByClassName("templateFilter")[0]
element.value = 'xxxxx'
but with proper component refresh?
2) Why is the {{ message }} not refreshed after calling set method?
Why is the message not refreshed after calling set method?
When you type something on your input the message data gets updated via an event handler BUT when you set it programmatically the event handler is NOT triggered and that's why message was not getting updated / refreshed....
Solution :
a short solution to your issue would be just to mutate the message value this.message = "xxxx" but if you insist on making that programmatically you have to trigger the input event :
set: function() {
let element = this.$refs.templateFilter
Vue.set(element, 'value', 'xxxx')
element.dispatchEvent(new Event('input'))
}
Here is a Demo : codepen
Beginner to JS and VueCLI so I'll try to explain as best as I can. I'm using Express as my back-end.
I'm trying to change the boolean in my array of objects on click. I'm able to accomplish that but when I click on a different list item in my v-for loop it's flipping the boolean in all other indexes of my array. Here's my code:
Express: /routes:
// fake data store
const tasks = [
{ id: 1, task: 't1', completed: false},
{ id: 2, task: 't2', completed: false},
{ id: 3, task: 't3', completed: false}
];
/**
* GET handler for /tasks route
* #returns {Array.<{id: Number, task: String, completed: Boolean}>} array of task objects
*/
router.get('/', (req, res) => {
res.send(tasks);
});
Webapp:
/**
* GET /tasks
* #returns Promise => {Array.<{id: Number, task: String, completed: Boolean}>} array of task objects
*/
export function getTasks() {
return request('tasks');
}
and now my Vue component:
<template>
<div id="tasks">
<h2>Movies to Add</h2>
<ul class="todo-list">
<li v-for='task in tasks' :id="task.id" v-on:click="completeMovie($event)" :key='task.id' class="todo-list__li">
<input class="todo-list__input" type="checkbox" :name='task.task' :id="task.task">
<div class="todo-list__checkbox">
<span class="todo-list__checkbox-inner"><i></i></span>
</div>
<label :for='task.task'>{{ task.task }}</label>
</li>
</ul>
</div>
</template>
<script>
import {getTasks} from './../../services/tasks';
export default {
name: 'TaskList',
data: function() {
return {
tasks: []
};
},
created: function() {
getTasks()
.then(res => this.tasks = res);
},
methods: {
completeMovie: function (event) {
var taskId = event.currentTarget.id -1;
getTasks()
.then((res) => {
this.tasks = res;
res[taskId].completed = !res[taskId].completed;
});
}
}
}
</script>
So when I click on my first list item it changes the Task: t1 to True but if I click on the second list item it changes t1 back to False and t2 to True. I'm not sure exactly what I'm doing wrong. I'm not even sure this is the best way to do this. My main issue is I'm not sure why it's happening.
Any help is much appreciated!
You're probably over-complicating this.
All you need is
<li v-for="task in tasks" :key="task.id"
#click="task.completed = !task.completed"
class="todo-list__li">
Demo ~ https://jsfiddle.net/xy1q0auL/2/
There's no (obvious) need to re-fetch the tasks every time you click on one. This is why your previous changes are reset; it's because you overwrite all the data with the unmodified values from getTasks().
When completeMovie() is called, you send an HTTP request to your server that is gonna send you back unaltered task list. I don't understand clearly what's you're trying to achieve but your callback in the promise has no sens. In the callback you reaffect the "tasks" list in your vue :
In this case, here is the event timeline you have to do :
When client page is loaded, Vue Initialized, the vue component calls your webservice to get the task list then print it.
Then when I click on a task, you have to make a another HTTP call to your webservice (on another route), that will update the task list and will return it.
Then in the call of your vue component you reaffect the new task list.
You can make its simply:
<li v-for='task in tasks' :id="task.id" v-on:click="completeMovie(task.id)" :key='task.id' class="todo-list__li">
<input class="todo-list__input" type="checkbox" :name='task.task' :id="task.task">
<div class="todo-list__checkbox">
<span class="todo-list__checkbox-inner"><i></i></span>
</div>
<label :for='task.task'>{{ task.task }}</label>
</li>
Ans for the method
completeMovie: function ($taskId) {
this.tasks[$taskId].completed = !this.tasks[$taskId].completed;
}
}
I am currently learning vue.js and having trouble accessing data in the methods.
data is loaded and set as a global variable (for now, this will probably change but not part of the problem now i think)
through ajax call this data is received:
data":[{"itemId":"58646f066803fa62388b4573","color":"#ffb878","name":"test1","startDate":"04/24/2017","work":"9.25"},{"itemId":"58646f066803fa62388b4572","color":"#ffb878","name":"test2","startDate":"04/24/2017","work":"4.25"},{"itemId":"58646f066803fa62388b4571","color":"#a4bdfc","name":"test3","startDate":"05/01/2017","work":"24.00"}]
which is set as a global (variable data is set outside of the functions) with:
...success: function (jsonObj)
{
data['item'] = jsonObj.data
....
now for the vue part:
var app = new Vue({
el:'#canvas',
data: {
items: data['item']
},
methods: {
moveItem: function(){
console.log("new date: "+this.startDate);
}
}
})
the html:
<div v-for="row in items" class="entirerow" v-bind:id="'row'+row.itemId">
<div class="itemrow">{{ row.name }}</div>
<div class="itemrow"><input type="text" v-model="row.startDate" #change="moveItem"></div>
<div class="itemrowlast">{{ row.work }}</div>
</div>
this nicely shows 3 rows with the correct data in each row. So far so good. But now if I change something in the input value the method moveItem is triggered but states "new date: undefined" in the console.log
I've tried console.log("new date: "+this.items.startDate) as well but no cigar and then it would seem the method wouldn't know which row is handled.
How can I access the correct data in the method, so from a certain row in the loop?
Thanks!
You refer to data object in method (this.startDate) not to item
moveItem: function(){
console.log("new date: "+this.startDate);
}
You can change your code like this
template
#change="moveItem(row)"
script
moveItem: function(row){
console.log("new date: " + row.startDate);
}