Skip to main content
Ask A Question
Notifications
You’re not receiving notifications from this thread.
Subscribe

Rails & Vue.js Trello Clone - Part 4 Discussion

General • Asked by Chris Oliver

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!

Yeah, that is true since we don't use it really anywhere else. 👍


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 ids 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">

hi, what event to use instead of @change now?

@Elijah, the @change is not working for me (the event is never fired), could you elaborate your solution please ?


Both the added and moved element now have the list_id, so that makes it easier.


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)


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 31,353+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.