※はじめからはこちら
前回までで、ボードのデータを表示することはできたので今回はカードの並び替えを実装していきます。
並び替え更新の処理をConsumerに追加
Consumerに、あるPipeLine内のCardIdのリストを受け取り、それで各CardのOrderを更新する処理を追加していきます。まずは、そのようなサービスを定義して行きます。
PipeLine, Cardモデルにメソッド追加
PipeLineやCardがID指定で取得できるほうが楽なので、そのようなクラスメソッドを定義します。
@@ -15,3 +15,10 @@ class PipeLine(models.Model):
@classmethod
def get_list_by_board(cls, board):
return list(cls.objects.filter(board=board).order_by('order'))
+
+ @classmethod
+ def get_by_id(cls, pipe_line_id):
+ try:
+ return cls.objects.get(id=pipe_line_id)
+ except cls.DoesNotExist:
+ return None
@@ -16,3 +16,10 @@ class Card(models.Model):
@classmethod
def get_list_by_pipe_line(cls, pipe_line):
return list(cls.objects.filter(pipe_line=pipe_line).order_by('order'))
+
+ @classmethod
+ def get_by_id(cls, card_id):
+ try:
+ return cls.objects.get(id=card_id)
+ except cls.DoesNotExist:
+ return None
update_card_orderの実装
PipeLineのIDと新しい並び順としてのCardIdのListを受け取り、その順にcard.order
を更新するサービスを追加します。
@@ -49,3 +49,17 @@ def get_board_data_by_board_id(board_id):
board_data['pipe_line_list'].append(pipe_line_data)
return board_data
+
+
+def update_card_order(pipe_line_id, card_id_list):
+ """
+ :param int pipe_line_id:
+ :param list card_id_list:
+ :return:
+ """
+ pipe_line = PipeLine.get_by_id(pipe_line_id)
+ for i, card_id in enumerate(card_id_list):
+ card = Card.get_by_id(card_id)
+ card.order = i
+ card.pipe_line = pipe_line
+ card.save()
これを呼び出せば、指定したPipeLine内のカードをまるっと更新できます。
Consumerの分岐処理を実装
Consumerがクライアントからメッセージを受け取る場合、JsonWebsocketConsumer
を継承しているクラスではreceive_json
というメソッドが呼び出されます。今後はクライアントから様々な種別のメッセージが届き、それごとに処理を実装していきますが、いずれもreceive_json
で受け取ることになります。
これを何も考えずに実装すると
def receive_json(self, content, **kwargs):
action_type = content['type']
if action_type == 'update_card_order':
self.update_card_order()
elif action_type == 'add_card':
self.add_card()
こんな感じになります。しかし、これでは処理が増えるたびにif文を伸ばしていくことになるのでよくありません。ということでこんな感じに実装します。
@@ -13,6 +13,9 @@ class KanbanConsumer(JsonWebsocketConsumer):
self.user = None
self.board_id = None
self.namespace = 'board'
+ self.action_map = {
+ 'update_card_order': self.update_card_order
+ }
def connect(self):
# 認証チェック
@@ -35,3 +38,17 @@ class KanbanConsumer(JsonWebsocketConsumer):
'namespace': self.namespace,
})
+ def update_card_order(self, content):
+ pass
+
+ def receive_json(self, content, **kwargs):
+ """
+ Typeに応じた処理を呼び出して実行する
+ :param dict content:
+ :param kwargs:
+ :return:
+ """
+ action = self.action_map.get(content['type'])
+ if not action:
+ raise Exception('{} is not a valid action_type'.format(content['type']))
+ action(content)
self.action_map
にどのメッセージが来たらどのメソッドを呼び出すかの対応を定義しておき、receive_json
ではメッセージ内のtype
に該当するものを直接呼び出します。また未定義のtype
を受け取った場合は例外を送出させます。これであれば、新しいメッセージを追加する際もaction_map
に追加するだけで済みます。
update_card_orderの組み込み
先程サービスとして作成したupdate_card_order
をConsumerに追加します。といってもaction_map
には先程追加したのでpass
となっている部分に組み込むだけです。
@@ -39,7 +39,19 @@ class KanbanConsumer(JsonWebsocketConsumer):
})
def update_card_order(self, content):
- pass
+ """
+ ボード内のカードの並び順を更新する
+ {
+ 'type': 'update_card_order',
+ 'pipeLineId': 1,
+ 'cardIdList': [3, 1]
+ }
+ :return:
+ """
+ pipe_line_id = content['pipeLineId']
+ card_id_list = content['cardIdList']
+ kanban_sv.update_card_order(pipe_line_id, card_id_list)
+ self.send_board_data()
これで一旦サーバ側の実装は終わりです。
並び替えの処理をフロント側に実装
Store.actionに呼び出しを追加
今しがたConsumerに追加したupdate_card_order
をStoreから呼び出せるようにします。
@@ -21,12 +21,26 @@ const getters = {
};
const actions = {
+ updateCardOrder({ commit, getters }, { pipeLineId, cardList }) {
+ console.log(pipeLineId, cardList);
+ const socket = getters.getSocket;
+ socket.sendObj({
+ type: 'update_card_order',
+ pipeLineId,
+ cardIdList: cardList.map(x => x.cardId),
+ });
+ },
};
このActionは更新対象のpipeLineId
と新しい並び順になったカードの一覧をcardList
として受け取ります。
そしてgetters.getSocket
でrootStoreに格納されているWebSocketのコネクションを取得します。そしてサーバに対してsendObj
でtype: update_card_order
を含んだメッセージを送信しています。これで、Consumer側のupdate_card_order
が呼び出されます。
ComponentにActionを組み込み
PipeLine.vue
にupdateCardOrder
を組み込みます。
@@ -27,9 +27,12 @@
</template>
<script>
+import { createNamespacedHelpers } from 'vuex';
import Draggable from 'vuedraggable';
import Card from './Card.vue';
+const { mapActions, mapGetters } = createNamespacedHelpers('board');
+
export default {
name: 'PipeLine',
@@ -59,6 +62,10 @@ export default {
},
set(value) {
console.log(value);
+ this.updateCardOrder({
+ pipeLineId: this.pipeLine.pipeLineId,
+ cardList: value,
+ });
},
},
pipeLineName() {
@@ -72,6 +79,12 @@ export default {
delPipeLineAction() {
window.confirm(`DELETE [${this.pipeLineName}] ? Are you sure?`);
},
+ ...mapActions([
+ 'updateCardOrder',
+ ]),
+ ...mapGetters([
+ 'getBoardId',
+ ]),
},
};
VueDraggableはD&D終了時に、v-modelに指定したwrappedCardList
のset
を呼び出しますので、その中でupdateCardOrder
を呼び出してやれば新しい並び順を渡すことができます。
これで、カードの並び替え自体はできるようになりました。
PipeLine内でも、PipeLineをまたがってもカードを移動できています。もちろんF5してもその並び順は保持されています。しかし、一瞬カードが前の位置に戻ってしまうチラ付きが出ています。これは次回解決していきます。