Drag and drop w AngularJS

Zbudujemy w AngularJS dwie listy, z których będziemy mogli metodą przeciągnij i upuść (drag and drop) zmieniać zawartość obu list.

AngularJS to biblioteka open source wspierana i firmowana przez Google. Służy on do szybkiego i łatwego budowania aplikacji internetowych. Model oparty o MVW (Model – View – Whatever) pozwala pogodzić idee JavaScript i modelu MVC. Co będzie nam potrzebne? Ponadto wykorzystamy również bootstrap i jquery. Zaczniemy od przygotowania pliku html. Będzie w nim sporo angularowej logiki. Dzięki temu plik z kodem javascript będzie minimalny.
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width; initial-scale=1.0">
        <title>AngularJS Drag and drop example 1</title>
        <link rel="stylesheet" href="css/bootstrap.min.css">
        <link rel="stylesheet" href="css/bootstrap-theme.min.css">
        <link rel="stylesheet" href="css/demo-framework.css">
        <link rel="stylesheet" type="text/css" href="css/simple.css?a">
		<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
		<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
        <script src="js/angular-drag-and-drop-lists.js"></script>
		<script type="text/javascript" src="js/list1.js?a"></script>
	</head>
    <body ng-app="demo" ng-controller="SimpleDemoController">
        <h1>AngularJS Drag and drop</h1>
        <div class="simpleDemo row">
        
            <div class="col-md-8">
                <div class="row">
                    <div class="col-md-6">
                        <div class="panel panel-info">
                            <div class="panel-heading">
                                <h3 class="panel-title">List A</h3>
                            </div>
                            <div class="panel-body">
                                <ul class="typeA" dnd-list="models.lists.A" dnd-disable-if="true">
                                    <li ng-repeat="item in models.lists.A"
                                        data-type="{{item.type}}"
                                        dnd-draggable="item"
                                        dnd-type="atype"
                                        dnd-moved="models.lists.A.splice($index, 1)"
                                        dnd-effect-allowed="copy"
                                        dnd-selected="models.selected = item"
                                        ng-class="{'selected': models.selected === item}"
                                        >
                                        {{item.label}}
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="panel panel-info">
                            <div class="panel-heading">
                                <h3 class="panel-title">List B</h3>
                            </div>
                            <div class="panel-body">
                                <ul class="typeB" dnd-list="models.lists.B">
                                    <li ng-repeat="item in models.lists.B"
                                        data-type="{{item.type}}"
                                        dnd-draggable="item"
                                        dnd-type="btype"
                                        dnd-moved=" models.lists.B.splice($index, 1)"
                                        dnd-effect-allowed="move"
                                        dnd-selected="models.selected = item"
                                        ng-class="{'selected': models.selected === item}"
                                        >
                                        <div ng-if="item.type === 'container'" class="panel-heading"><h3 class="panel-title">
                                            {{item.label}}
                                        </h3></div>
                                        {{item.label}} <span ng-if="item.type === 'container'">content</span>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        
            <div class="col-md-4">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <h3 class="panel-title">Generated Model</h3>
                    </div>
                    <div class="panel-body">
                        <pre class="code">{{modelAsJson}}</pre>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
Widać, że kodu jest dość sporo. Nie ma co się jednak przerażać bo większość jest bardzo prosta do wyjaśnienia. W sekcji head dodajemy pliki css i js. Najważniejsze to załączenie jquery, angularJS oraz angular-drag-and-drop-lists.js. Nasz kod javascript będziemy umieszczać w pliku list1.js.
Do tagu body dodajemy dwa atrybuty ng-app i ng-controller. Określamy w ten sposób, gdzie zaczyna się nasza aplikacja oraz nazwę kotrolera (tej nazwy będziemy używać w pliku js).
Dalej w kodzie mamy podział na dwa panele (dwa divy) A i B. Z panelu A będzie można przeciągać elementy na panel B. Nie można przeciągać elementów na panel A (zaimplementowałam to specjalnie, ale wcale tak nie musi być - atrybut dnd-disable-if listy). Elementy trzymamy w listach ul. Elementy listy oprócz standardowych angularowych atrybutów np. ng-repeat posiadają specjalne atrybuty dla funkcjonalności drag and drop. Są to: dnd-draggable, dnd-moved, dnd-effect-allowed itd. Najważniejszym atrybutem listy jest dnd-list, który określa javascriptową tablicę będącą naszym modelem dla listy. Kod javascript pliku list1.js poniżej:
angular.module("demo", ["dndLists"]).controller("SimpleDemoController", function($scope) {

    $scope.models = {
        selected: null,
        lists: {"A": [], "B": []}
    };

    // Generate initial model
    for (var i = 1; i <= 3; ++i) {
        $scope.models.lists.A.push({label: "Item A" + i, type: "item"});
        $scope.models.lists.B.push({label: "Item B" + i, type: "item"});
    }
    $scope.models.lists.A[2].type="container";
    
    $scope.models.lists.B[1].type="container";
    // Model to JSON for demo purpose
    $scope.$watch('models', function(model) {
        $scope.modelAsJson = angular.toJson(model, true);
    }, true);
});
Jak widać ładujemy dyrektywy dndLists. Następnie przygotowujemy startową zawartość list. Końcowy kod sprawia, że w htmlu będziemy mieć dostępną zmienną modelAsJson pokazującą jak zmieniają się modele list. Demo działania list