Ask A Question

Notifications

You’re not receiving notifications from this thread.

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!

Reply

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

Reply

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 :(

Reply

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.

Reply

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
Reply

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

Reply

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

Reply

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

Reply

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

Reply

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)

Reply

Instead of

<draggable v-model="lists.card"

Use

<draggable v-model="list.cards"
Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 82,329+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.