Rails & Vue.js Trello Clone - Part 4 Discussion
I am loving this series! This is such a cool project. The combination of acts_as_list and Vue makes it seem effortless.
I had one minor suggestion for the code in the cardMoved function. Rather than finding the list index, would it be better to just set a constant to the new list itself, and then use that object to set the list_id in the ajax request? Here's what I did:
const card_list = this.lists.find((list) => {
return list.cards.find((card) => {
return card.id === element.id
})
});
Then when setting up data, I did this, which seems a bit cleaner:
data.append("card[list_id]", card_list.id);
Thanks for the great material!
They updated the draggable
package and sadly this no longer works. The end
method is completely different response now. I spent a few hours trying to get the JS IDs for the card and list but very frustrating :(
Have any information on that? It looks like the Draggable docs (https://github.com/SortableJS/Sortable#event-object-demo, search for "onEnd") show the following object.
// Element dragging ended
onEnd: function (/**Event*/evt) {
var itemEl = evt.item; // dragged HTMLElement
evt.to; // target list
evt.from; // previous list
evt.oldIndex; // element's old index within old parent
evt.newIndex; // element's new index within new parent
evt.oldDraggableIndex; // element's old index within old parent, only counting draggable elements
evt.newDraggableIndex; // element's new index within new parent, only counting draggable elements
evt.clone // the clone element
evt.pullMode; // when item is in another sortable: `"clone"` if cloning, `true` if moving
},
It's been a while since I did this, so if this changed, then it may need a couple tweaks to match up, but at least here you have information on all the properties from the event.
Unfortunately the new response doesn't return the element
that was moved, just the indexes.
The to
and from
attributes that are returned are HTML / DOM elements, not the javascript objects, so I can't find the id
s needed.
CustomEvent {isTrusted: false, to: div.dragArea, from: div.dragArea, item: div.card.card-body.mb-3, clone: div.card.card-body.mb-3, …}
bubbles: true
cancelBubble: false
cancelable: true
clone: div.card.card-body.mb-3
composed: false
currentTarget: null
defaultPrevented: false
detail: null
eventPhase: 0
from: div.dragArea
isTrusted: false
item: div.card.card-body.mb-3
newDraggableIndex: 0
newIndex: 0
oldDraggableIndex: 0
oldIndex: 0
originalEvent: DragEvent {isTrusted: true, dataTransfer: DataTransfer, screenX: 695, screenY: 335, clientX: 695, …}
path: (8) [div.dragArea, div.col-3, div.row.dragArea, div.container.mt-5, body, html, document, Window]
pullMode: true
returnValue: true
srcElement: div.dragArea
target: div.dragArea
timeStamp: 8222.279999987222
to: div.dragArea
type: "end"
__proto__: CustomEvent
Ugh, I was looking in the wrong spot. They have a new method (documented separately from the other methods) that has all the data needed.
Tutorial is fine as is with one edit:
From:
<draggable v-model="list.cards" group='cards' class="dragArea" @end="cardMoved">
To:
<draggable v-model="list.cards" group='cards' class="dragArea" @change="cardMoved">
@Elijah, the @change
is not working for me (the event is never fired), could you elaborate your solution please ?
Hey Chris or anybody,
I am stuck here, the @change
does not fire any event and the @end
or others hooks does not provide the element in the event.
Without the element, it's pretty impossible to go further.
Thanks for you answer.
The @end
event:
CustomEvent {isTrusted: false, to: div.dragArea, from: div.dragArea, item: div.card.card-body.card-padding, clone: div.card.card-body.card-padding, …}
isTrusted: false
detail: null
type: "end"
target: div.dragArea
currentTarget: null
eventPhase: 0
bubbles: true
cancelable: true
defaultPrevented: false
composed: false
timeStamp: 3530.1349999936065
srcElement: div.dragArea
returnValue: true
cancelBubble: false
path: (7) [div.dragArea, div.col-3, div#app.row.dragArea, body.lang-en, html, document, Window]
to: div.dragArea
from: div.dragArea
item: div.card.card-body.card-padding
clone: div.card.card-body.card-padding
oldIndex: 1
newIndex: 0
oldDraggableIndex: 1
newDraggableIndex: 0
originalEvent: DragEvent {isTrusted: true, dataTransfer: DataTransfer, screenX: 814, screenY: 471, clientX: 789, …}
pullMode: undefined
__proto__: CustomEvent
<template>
<draggable v-model="lists" :options="{group: 'lists'}" class="row dragArea" id="app" @end="listMoved">
<div class='col-3' v-for="(list, index) in lists">
<h6>{{ list.name }}</h6>
<hr>
<draggable v-model="lists.card" :options="{group: 'cards'}" class="dragArea" @end="cardMoved">
<div class="card card-body card-padding" v-for="(card, index) in list.cards" :key="card.name">
{{ card.name }}
</div>
</draggable>
<div class="card card-body">
<textarea v-model="messages[list.id]" name="new_card" class="form-control"></textarea>
<button @click="submitMesages(list.id)" class="btn btn-secondary">Add new card</button>
</div>
</div>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: { draggable },
props: ["original_lists"],
data: function() {
return {
messages: {},
lists: this.original_lists
}
},
methods: {
log: function(event) {
console.log(event)
},
listMoved: function(event) {
let data = new FormData
data.append("list[position]", event.newIndex + 1)
Rails.ajax({
url: `/lists/${this.lists[event.newIndex].id}/move`,
type: "PATCH",
data: data,
dataType: 'json'
})
},
cardMoved: function(id, event) {
console.log(id, event)
//
// const list_index = this.lists.findIndex((list) => {
// return list.cards.find((card) => {
// console.log(event)
// // return card.id === event.element.id
// })
// })
},
submitMesages: function(list_id) {
let data = new FormData
data.append("card[list_id]", list_id)
data.append("card[name]", this.messages[list_id])
Rails.ajax({
url: "/cards",
type: "POST",
data: data,
dataType: 'json',
success: (data) => {
const index = this.lists.findIndex(item => item.id == list_id)
this.lists[index].cards.push(data)
this.messages[list_id] = undefined
}
})
}
}
}
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
.dragArea {
padding: 5px;
min-height: 20px;
}
.card-padding {
margin-bottom: 10px
}
.ghost {
opacity: 0.8;
background: #f4f4f4;
}
</style>
Config:
'rails', '5.2.4.1'
"vuedraggable": "2.23.2"
webpacker (4.2.2)