+4

AngularJS Drag & Drop with HTML5

Introduction

Chắc hẳn chúng ta cũng không ai xa lạ gì khi nghe đến Angular nữa, nó là 1 framework rất nổi tiếng củaJavaScripts với nhiều ưu điểm như: được phát triển bởi Google và là mã nguồn mở viết theo mô hình MVC, cơ chế data-binding 2 chiều, cho phép xây dựng ngay trong trình duyệt giúp code sạch và gọn... và rất nhiều ưu điểm khác. Nếu quan tâm các bạn có thể tìm hiểu thêm, hiện nay có rất nhiều bài viết từ nhiều nguồn khác nhau viết rất chi tiết về framework khá phổ biến này. Nay mình sẽ giới thiệu về 1 directive khá hay của AngularJS, đó là dnd-draggable. Nói nôm na thì dnd-draggable là 1 Angular directive cho phép chúng ta modify 1 list nào đó kết hợp với tính năng drag and drop API của HTML5.

Download & Installation

Việc đầu tiên chúng ta cần làm sau khi đã cài AngularJS vào hệ thống, đó là add thêm thư viện angular-drag-and-drop-lists.js tại đây, sau đó tiến hành include nó vào trong app của bạn:

//= require lib/angular-drag-and-drop-lists

sau đó chúng ta sẽ add thêm module dndLists vào trong angular app, giả sử ta sẽ có myApp như sau:

(function() {
  angular.module('myApp', ['ui.bootstrap', 'ui.bootstrap.datetimepicker', 'datetimePickerLib', 'dndLists'])
    .config(['$httpProvider', '$locationProvider', defaultConfig]);
})();

Vậy là xong phần download và cài đặt. Tiếp theo chúng ta sẽ xem cách dùng nó như thế nào.

How to use

Ta sẽ xây dựng Angular controller có tên demoDragAndDropList với 2 lists cơ bản như sau:

'user strict;'

angular.module('managerApp').controller('demoDragAndDropListController', demoDragAndDropListController);
function demoDragAndDropListController() {
  var vm = this;
  var originalList = [{name: "A1"}, {name: "A2"}, {name: "A3"}, {name: "A4"}];
  var dragList = [{name: "B1"}, {name: "B2"}, {name: "B3"}, {name: "B4"}];
  vm.fields = {
    selected: null,
    lists: {originalList, dragList}
  };
}

Chúng ta cũng có thể dùng $scope, tuy nhiên việc lạm dụng biến global đôi khi khiến chúng ta gặp khó khăn trong quá trình maintain, chi tiết các bạn có thể tham khảo thêm về $scope trong AngularJS. Vì thế ở đây mình sẽ dùng vm. Sau đó là phía bên View:

<div class="simpleDemo" ng-controller="demoDragAndDropListController as vm">
  <div class="col-md-12">
    <ul dnd-list="vm.fields.lists.originalList" class="col-md-4">
      <li ng-repeat="item in vm.fields.lists.originalList" dnd-draggable="item" dnd-moved="vm.fields.lists.originalList.splice($index, 1)"
        dnd-effect-allowed="move" dnd-selected="vm.fields.selected = item" ng-class="{'selected': vm.fields.selected === item}">
        {{item.name}}
      </li>
    </ul>

    <ul dnd-list="vm.fields.lists.dragList" class="col-md-4">
      <li ng-repeat="item in vm.fields.lists.dragList" dnd-draggable="item" dnd-moved="vm.fields.lists.dragList.splice($index, 1)"
        dnd-effect-allowed="move" dnd-selected="vm.fields.selected = item" ng-class="{'selected': vm.fields.selected === item}">
        {{item.name}}
      </li>
    </ul>
  </div>
</div>

Sau đó là 1 chút css cho đẹp nếu muốn 😄

/**
 * The dnd-list should always have a min-height,
 * otherwise you can't drop to it once it's empty
 */
.simpleDemo ul[dnd-list] {
    min-height: 42px;
    padding-left: 0px;
}

/**
 * The dndDraggingSource class will be applied to
 * the source element of a drag operation. It makes
 * sense to hide it to give the user the feeling
 * that he's actually moving it.
 */
.simpleDemo ul[dnd-list] .dndDraggingSource {
    display: none;
}

/**
 * An element with .dndPlaceholder class will be
 * added to the dnd-list while the user is dragging
 * over it.
 */
.simpleDemo ul[dnd-list] .dndPlaceholder {
    background-color: #ddd;
    display: block;
    min-height: 42px;
}

.simpleDemo ul[dnd-list] li {
    background-color: #fff;
    border: 1px solid #ddd;
    border-top-right-radius: 4px;
    border-top-left-radius: 4px;
    display: block;
    padding: 10px 15px;
    margin-bottom: -1px;
}

/**
 * Show selected elements in green
 */
.simpleDemo ul[dnd-list] li.selected {
    background-color: #dff0d8;
    color: #3c763d;
}

Các bạn có thể xem và demo trực tiếp tại đây. Ok, vậy là chúng ta đã cơ bản sử dụng được directive dnd-draggable. Phần code ở view đại khái là thế này:

  • dnd-draggable directive sẽ chịu trách nhiệm di chuyển 1 element A nào đó của thẻ HTML, và di chuyển cả object được gán cùng với element A đó. Sau khi element A này được di chuyển, dnd-moved sẽ đóng vai trò xóa bỏ nó khỏi list ban đầu( ở đây chúng ta dùng method splice để loại bỏ phần tử được select khỏi mảng cũ).

  • dnd-selected là callback sẽ được invoke mỗi khi có element được click nhưng không được drag.

    Có 1 chút lưu ý nhỏ là 2 lists mình build đều là mảng chứa các hash, chắc hẳn sẽ có câu hỏi là tại sao không là 1 mảng bảo gồm các item luôn cho tiện vd như var list1 = [A1, A2, A3,..]. Lúc đầu mình xem demo cũng đặt ra câu hỏi như vậy và đã test thử, tuy nhiên sau đó mình gặp trường hợp lỗi về duplicate keys Error: ngRepeat:dupes khi Angular dùng các keys này để tạo ra DOM cùng với các items ở trong ngRepeat expression. Giải pháp ở trong trường hợp này nếu không muốn phân biệt qua keys của hash, chúng ta cũng có thể dùng track by, có thể track bằng $index hay 1 properties nào đó của item hiện tại để có được các unique keys. Nhưng trong trường hợp này chỉ mô phỏng lại 1 list khá đơn giản thôi nên mình nghĩ cũng không cần phải cầu kì như vậy, nên dùng hash luôn.

Summary

Bài viết của mình nhằm chia sẻ 1 chút kiến thức hạn hẹp của bản thân về dnd-draggable - 1 directive khá hay của AngularJS. Cảm ơn bạn đã dành thời gian đọc bài viết. Nguồn: https://github.com/marceljuenemann/angular-drag-and-drop-lists


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí