My apps structure looks something like this
App
Column Component
Task Component
Task Component
Task Component
Column Component
Task Component
Task Component
Column Component
Column Component
Each column has a <draggable> component surrounding the tasks.
At the App level, I have a data object with the different colums and each column with its tasks.
data: function() {
return {
columns: {
new: [
{
ID: 1,
title: 'The thing',
description: 'Prepare a proposal for facade'
},
{
ID: 2,
title: 'The thing 2',
description: 'Prepare a proposal for facade'
},
{
ID: 3,
title: 'The thing 3',
description: 'Prepare a proposal for facade'
}
],
inProgress: [
{
ID: 4,
title: 'The thing 4',
description: 'Prepare a proposal for facade Store Opening Process (DEMO)'
},
{
ID: 5,
title: 'The thing 5',
description: 'Prepare a proposal for facade Store Opening Process (DEMO)'
}
],
done: [],
onHold: []
}
}
}
I loop over the columns, and bind the object to the component:
<kool-column
v-for="column"
v-bind="colum"
></kool-column>
The Kool Column component:
<script id="kool-column-template" type="text/x-template">
<div>
<div>
{{ column.name }} ({{ column.cards.length }})
</div>
<div>
<draggable v-model="cards" group="columnkool">
<div v-for="card in cards" :key="card.ID">
<kool-card v-bind="card"></kool-card>
</div>
</draggable>
</div>
</div>
</script>
<script>
$(document).ready(function() {
Vue.component('koolColumn', {
template: '#kool-column-template',
props: {
ID: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
cards: {
type: Array,
default: function() { return []; }
}
}
});
});
</script>
So, my problem, when I drag and drop the cards, I get a Vue error/warning:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "cards"
Makes sense, I have been reading and investigating and in Vue, you want to use props to pass data down into the components, but then, to modify this data, you send an event to the parent, and let the parent modify it.
Ok, but the library I am using Vue Draggable modifies the list I pass it. So, what should I do? Solutions that I thougt about:
- On mounting the column component, copy the prop cards into a data of the component, use this for the list and modifications, and everytime it is changed, send to the parent the new list. I don't think this makes much sense, I will have two sources of information (the cards in the app object and the cards in my column component object), and risk that they are not aligned, after all, I am updating the list separately.
- When droping an item, use the library to actually "not allow" the drop, (you can use the library events and return false so it does not actually change the lists). Catch the event, send to the parent the info of what item was dragged and were the user tried to drop it and change the data accordingly. Basically, I would be using the drag and drop library just to know where the user tried to drop something, and actually changing the data manually. This also seems strange, the code is really hard to follow and there is a flicker when droping an item (because it goes back to its original position and then to where you wanted to have it).
- Another bad solution is to pass to the column component, not a v-bind and each property individually, but the whole object. So instead of
v-bind="colum"I do:column="column"which actually makes the Vue Warning disappear and keeps the double data binding. The thing is, I am doing the anti-pattern, without the warning, but still... (btw, this approach comes from this example: Task management using Vue)
Should I go on with the approach #2?, ugly but at least not an anti-pattern and not doubling the info. Or is there another way of keeping the double data binding when I am not in "control" of the list being updated? Or maybe just a different architecture/structure for the app?.
