A compiled and mounted tag doesn't seem to update - riot.js

After compiling and mounting a tag which reads an opts property passed down from it's parent, when the passed down opt is updated, I cannot get the child reading the property to update.
To see a demo of the problem run:
git clone https://github.com/shouston3/learn-riot.git && cd learn-riot/demo-problem
npm i && npm start
visit http://localhost:3333
When you increment the counter, the uncompiled counter will update,
But the compiled one wont
How can I get this to update?
Thanks in advance for the help

I simplified your code assuming that you want to increment both parent and child tag. I removed the var self=this, than in this case wasn't needed, and changed this assignation self.opts.count = ++self.opts.count; to this.count = ++this.count
The problem is that you tried to assign the data to the opts, that's not how it works. To pass data to the children you just pass the params in the tag <count count={this.count}></count> (probably is better to use different names for tags and variables)
Here is the code
<demo>
<button onclick={increment}>increment</button>
<h1>Uncompiled count: {this.count}</h1>
<count count={this.count}></count>
this.count = 0;
increment () {
this.count = ++this.count;
console.log('count: ', this.count);
this.update();
}
</demo>
Here is the example
https://jsfiddle.net/vitomd/mjqa2d5j/4/

Related

Add value at the beginning of an array in react-native hook

I am functional component I am using hooks to update the state of the array locationData
const c = {
title: inputValue,
}
setLocationData([...locationData, c]);
This is working fine, but now I want to add the value at the beginning of the array, and not at the end.
Edit:
I also have a problem to delete an item from the array. I want to delete one item, but more are deleted
const deleteItem = (index) => {
console.log(index)
var temp = locationData;
var temp = locationData.splice(index, 1);
setLocationData(temp);
}
You are almost there, just switch the position in the array.
setLocationData([c, ...locationData]);
You should also be aware of other methods like splice, slice, push, pop etc...
Update: Using Splice
This relates to part 2 of your question with regards to removing from specific index.
The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place. To
access part of an array without modifying it, see slice().
So your codes should be similar to the following
var temp = [...locationData];
temp.splice(index, 1);
setLocationData(temp);

Why does data in list1 changed automatically when I change the data in the other list2 which is cloned from the list1? Using Vue.Draggable

I am using Vue.Draggable in my Vue project and trying to drag(clone) a button from sider into a draggable component.
I want to modify the data when I clone the button into the component. But I find that when I modify the data in the list which is binded with the component, the original list that sider used got changed automatically.
Is there some kind of synchronization mechanism in Vue.Draggable or something? I want to change the object data in the component only.
I tried to modify the object in the list2 manually by using a vue extension in Chrome browser. And it still happens. So I think maybe it's not bug in my code.
addEntity (conditionID, entity) {
if (!entity.forChoose) {
}
else {
let variable = 0;
for (let i = 0, len = this.whens.length; i < len; i++) {
if (this.whens[i].entity[0].id == entity.id) {
variable++;
}
}
this.whens[conditionID].entity[0].forChoose = false;
this.whens[conditionID].entity[0].variable = variable;
this.whens[conditionID].entity[0].name = entity.name + '-fake';
}
},
The code above is the event when I drag the data into the component, and changed some variable.
Although I did nothing to the original data in the Sider where I cloned the data from, it still got changed as well.
Is there a way to change the dragged data but do not affect the original data?
Please help me, thank you!
I think this is happening because of the immutability.
so can you try using spread operator to create new shallow copy of your list before you change it.
how to use spread operator (triple dot)
let newArrayOfWhens = [...this.whens]
then use the "newArrayOfWhens" array in your code
You can create a deep clone as well if you want, but try to avoid it if it is not necessary.
you can use a library call "lodash" to do it very easily
deepClone

My Vuex getter is not getting automatically updated.. how I can debug it?

I'm using Vuex with a getter that filters a lot of data and then some components present it to the user grouped by status. The user can increment the visible count of elements per status by 5. How many items are visible currently is on the Vuex store and a getter uses this to create a "View object".
When I update this visibility object the getter is no rerun so something in the dependency tracking went south. I'm not adding or deleteing properties, still I'm using Vue.set(...) just to be sure.
This is the mutation that increments the visible amount of items for a status:
viewMore(state, status) {
console.log('viewMore')
const current = state.visibility.statuses[status]
Vue.set(state.visibility.statuses, status, current + 5)
}
This mutation is running well and I can see in the developer tools how the visibility increments reactively with every commit. Now here is the getter that depends on this data:
visibleProspects(state, getters) {
console.log('visibleProspects')
let result = {}
for (const status in getters.sourceData) {
if (!result[status]) {
result[status] = { prospects: [] }
}
getters.sourceData[status].forEach(function(prospect) {
if (result[status].prospects.length < state.visibility.statuses[status])
result[status].prospects.push(prospect)
})
}
return result
}
What this does is traverses a complex getter named sourceData (not shown here for brevity) and then depending on how many visible items there are it returns a new structure with that maximum in an array. visibleProspects is then used by my components and everything runs fine the first time or if a update the data that sourceData computes (e.g adding / editing / deleting a prospect).. but no matter what I do modifying state.visibility.statuses is not forcing visibleProspects to recompute.
How can I debug this?
You can make deep copy to make it reactive (using JSON.parse(JSON.stringify())
viewMore(state, status) {
console.log('viewMore')
const current = state.visibility.statuses[status]
state.visibility.statuses[status] = current + 5
state.visibility = JSON.parse(JSON.stringify(state.visibility))
}
#ittus 's answer should work. But the clone operation would be heavy if your state is big.
alternatively, you may try using Vue.set on the root state state.visibility instead. This should make the reactivity works as expected.
Vue.set(state.visibility, 'statuses', {
...state.visibility.statuses,
[status]: current + 5
})

Changing complex computed object in vue.js

I have complicated object for a table. Looks like this:
{
1510002000: {
date: "07.11.17"
hours: {
1510002000:{
activity:{
activity: "Тест",
color: "#00ff00",
end_at: 1510005600,
start_at: 1510002000,
type_id: 1
}
},
1510005600: {
...
},
...
}
},
....
}
This is a code from template that uses this object:
<tr v-for="date in this.tds">
<td>{{ date.date }}</td>
<td is="hour-td"
v-for="hour in date.hours"
:color="hour.activity.color"
:start_at="hour.activity.start_at"
:end_at="hour.activity.end_at"
:activity="hour.activity.activity"
:type_id="hour.activity.type_id"
>
</td>
</tr>
I evaluated it as a computed property, but I need to rerender table when parent component provides data assync, so I have a watcher for prop (prop called "activities"):
watch: {
activities: function(){
var vm = this;
let dth = new DateTimeHelper;
if (this.activities.length > 0){
this.activities.forEach(function(activity){
let dateTimestamp = dth.getDateTimestampFromTimestamp(activity.start_at); // just getting the key
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
});
}
console.log(vm.tds) // here I can see that the object is filled with new data
}
},
The problem is that the table doesn't rerender. More precisely, the component "hour-td" does not contain the new data.
Also I've tried to use Vue.set, but no success with that
Can you help me with the updating table? I've spent like 5 hours for refactoring and attempts.
Thanks in advance
SOLUTION
In my case there can be two states: there are activities, there are no activities. So I made two computed props for each case and render them separately and switch by v-if="activities.length"
I think that your problem is with Vue known issue for Change Detection Caveats (you can read here) with array direct assignation, that don't detect changes.
You should change this part of code (with direct array assignation):
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
With the Vue.set() option for arrays in order to detect the change and re-renders the component. It worked for me in differents occassions:
// Vue.set(object, key, value)
// something like this:
Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity.activity,1,activity.activity);
More info here: https://codingexplained.com/coding/front-end/vue-js/array-change-detection
Edit:
I see now that you said:
Also I've tried to use Vue.set, but no success with that
What you mean with: "no success" ? Can you share the code? I have the same issue and I resolved with Vue.set..
You can also take a look to vm.$forceUpdate(), try to execute after the last console.log or grab all the code inside a vm.$nextTick( [callback] ) in order to execute all the actions (load data in the table) and then, re-render the component on next tick.
More info here: https://v2.vuejs.org/v2/api/#vm-forceUpdate && https://v2.vuejs.org/v2/api/#Vue-nextTick
Edit 2:
I think that your problem is with the index of the array, you should take a look here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection .
Try changing the:
if (vm.tds[dateTimestamp]){
if (vm.tds[dateTimestamp].hours[activity.start_at]){
vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
vm.tds[dateTimestamp].hours[activity.start_at].activity.color = activity.color;
vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id = activity.type_id;
}
}
and simplify with:
if (vm.tds[dateTimestamp] && vm.tds[dateTimestamp].hours[activity.start_at]){
Vue.set( vm.tds, vm.tds.indexOf(vm.tds[dateTimestamp].hours[activity.start_at]), activity);
}
Hope it helps!

How to 'watch' newly created rows in local storage in VueJs?

I am working to create a grid like component in VueJs. You can see the JSFiddle here: https://jsfiddle.net/masade/nrb9f4j2/
In the above fiddle the <datacell> component is used to allow inline edit for any cell.
I am 'watch'-ing changes to the rows and saving them in local storage
A newly created row is being pushed to parent object by this.$parent.unshift(newrow) in the <addrow> component
The problem is when I add a new row, though it is being added to the rows local storage it is not being watched for changes
Steps to replicate the issue (on JS fiddle):
Click on Add Row and enter a name to add
Once the row is added, click on the second column (year) and update it
Any changes to newly added row is not being updated
However if you refresh the page, and modify the same again, I am able to update it again.
Please help me understand if I am doing something wrong
You stumbled into one of the change detection caveats in Vue. Vue cannot detect when you have added a property to an object using the index of that object.
You define newrow like this:
data: function(){
return {
newrow : {}
}
},
which means that newrow has no properties. After that, you always add properties to newrow using an index, like here:
this.$parent.cols.map(function(col,index){
if(typeof newrow[col.m] == "undefined")
newrow[col.m] = "";
})
This means that Vue doesn't know about any of the properties you just added.
I added a method to your fiddle called makeNewRow which adds the properties you need to newrow correctly using the $set method.
makeNewRow(){
this.newrow = {};
this.$parent.cols.map((col,index) => this.$set(this.newrow, col.m, null))
},
Then I call it in mounted and in your addRow method. This fixes your bug. Here is the updated fiddle.
Note: there was an additional bug in your code that I fixed. If someone opened your app and did not previously have grid-vue-local in their local storage, then the uid for gridStorage would be zero, and the first row they added would have the id 0. To fix this I simply added
save: function (rows) {
gridStorage.uid = rows.length
localStorage.setItem(STORAGE_KEY, JSON.stringify(rows))
}
data(){
return{
items:localStorage.fetch()
}
}
watch:{
items:{
handler:function(items){
localStorage.save(items);
},
deep:true
}
}
you can save in items,and watch items,if the items has changed,it will updata