如何扩展属性指令以更新数据模型?

时间:2017-04-27 15:52:28

标签: javascript angularjs

我目前正在使用这个名为angular-multiple-selection的指令,它很棒(并且是我能找到的唯一一种)但在事件触发后似乎没有提供所选元素的列表。我需要在选择一个或多个元素后更新我的模型,然后让模型更新指令。

Here's a plucker demonstrating the issue.

INDEX.HTML

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="directive.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" />
    <link rel="stylesheet" href="style.css" />

    <script id="content-template" type="text/ng-template">
      <h4>Lasso, click or [Shift] + click to select fields</h4>
      <div class="item_container" multiple-selection-zone>
          <div ng-repeat="f in model.transaction.fields" multiple-selection-item class="well" ng-class="{'selecting': isSelecting ,'selected': isSelected}">{{f.label}}</div>
      </div>
      <h4>I need the data model "selected" properties below to update when the related field is selected above.</h4>
      <pre>{{model | json}}</pre>
      <h4>And then when the data model updates, I need the directive to update the selected fields appropriately as well (i.e. using the select/deselect all buttons below).</h4>
      <button type="button" class="btn btn-default" ng-click="model.selectAll()">Select All</button>
      <button type="button" class="btn btn-default" ng-click="model.deselectAll()">Deselect All</button>
    </script>

    <script src="script.js"></script>
  </head>

  <body>
    <div class="container">
      <content-component></content-component>
    </div>
  </body>

</html>

的script.js

console.clear();

function contentController(TransactionFactory) {
  var model = this;
  model.transaction = TransactionFactory;

  model.selectAll = function() {
    //mark all fields as selected
    for (var i = 0; i < model.transaction.fields.length; i++) {
      model.transaction.fields[i].selected = true;
    }
  }

  model.deselectAll = function() {
    //deselect all fields that are selected
    for (var i = 0; i < model.transaction.fields.length; i++) {
      model.transaction.fields[i].selected = false;
    }
  }
}

var app = angular.module("app", ['multipleSelection']);

app.factory('TransactionFactory', function () {
    var transaction = {
        fields: [
          {label: "field 1", selected: false}, 
          {label: "field 2", selected: false}, 
          {label: "field 3", selected: false}, 
          {label: "field 4", selected: false}, 
          {label: "field 5", selected: false}
        ]
    };
    return transaction;
});

app.component("contentComponent", {
    template: $("#content-template").html(),
    controllerAs: "model",
    controller: ["TransactionFactory", contentController]
});

DIRECTIVE.JS

/**
 * Angular JS multiple-selection module
 * @author Maksym Pomazan
 * @version 0.0.3
 * https://www.npmjs.com/package/angular-multiple-selection
 */
function getSelectableElements(element) {
    var out = [];
    var childs = element.children();
    for (var i = 0; i < childs.length; i++) {
        var child = angular.element(childs[i]);
        if (child.scope().isSelectable) {
            out.push(child);
        } else {
            if (child.scope().$id!=element.scope().$id && child.scope().isSelectableZone === true) {

            } else {
                out = out.concat(getSelectableElements(child));
            }
        }
    }
    return out;
}

function offset(element) {
    var documentElem,
        box = {
            top: 0,
            left: 0
        },
        doc = element && element.ownerDocument;
    documentElem = doc.documentElement;

    if (typeof element.getBoundingClientRect !== undefined) {
        box = element.getBoundingClientRect();
    }

    return {
        top: box.top + (window.pageYOffset || documentElem.scrollTop) - (documentElem.clientTop || 0),
        left: box.left + (window.pageXOffset || documentElem.scrollLeft) - (documentElem.clientLeft || 0)
    };
}

angular.module('multipleSelection', [])
    .directive('multipleSelectionItem', [function() {
        return {
            scope: true,
            restrict: 'A',
            link: function(scope, element, iAttrs, controller) {

                scope.isSelectable = true;
                scope.isSelecting = false;
                scope.isSelected = false;

                element.on('mousedown', function(event) {
                    if (element.scope().isSelected) {
                        if (event.ctrlKey) {
                            element.scope().isSelected = false;
                            element.scope().$apply();
                        }
                    } else {
                        if (!event.ctrlKey) {
                            var childs = getSelectableElements(element.parent());
                            for (var i = 0; i < childs.length; i++) {
                                if (childs[i].scope().isSelectable) {
                                    if (childs[i].scope().isSelecting === true || childs[i].scope().isSelected === true) {
                                        childs[i].scope().isSelecting = false;
                                        childs[i].scope().isSelected = false;
                                        childs[i].scope().$apply();
                                    }
                                }
                            }
                        }
                        element.scope().isSelected = true;
                        element.scope().$apply();

                    }
                    event.stopPropagation();
                });
            }
        };
    }])
    .directive('multipleSelectionZone', ['$document', function($document) {
        return {
            scope: true,
            restrict: 'A',
            link: function(scope, element, iAttrs, controller) {

                scope.isSelectableZone = true;

                var startX = 0,
                    startY = 0;
                var helper;

                /**
                 * Check that 2 boxes hitting
                 * @param  {Object} box1
                 * @param  {Object} box2
                 * @return {Boolean} is hitting
                 */
                function checkElementHitting(box1, box2) {
                    return (box2.beginX <= box1.beginX && box1.beginX <= box2.endX || box1.beginX <= box2.beginX && box2.beginX <= box1.endX) &&
                        (box2.beginY <= box1.beginY && box1.beginY <= box2.endY || box1.beginY <= box2.beginY && box2.beginY <= box1.endY);
                }

                /**
                 * Transform box to object to:
                 *  beginX is always be less then endX
                 *  beginY is always be less then endY
                 * @param  {Number} startX
                 * @param  {Number} startY
                 * @param  {Number} endX
                 * @param  {Number} endY
                 * @return {Object} result Transformed object
                 */
                function transformBox(startX, startY, endX, endY) {

                    var result = {};

                    if (startX > endX) {
                        result.beginX = endX;
                        result.endX = startX;
                    } else {
                        result.beginX = startX;
                        result.endX = endX;
                    }
                    if (startY > endY) {
                        result.beginY = endY;
                        result.endY = startY;
                    } else {
                        result.beginY = startY;
                        result.endY = endY;
                    }
                    return result;
                }

                /**
                 * Method move selection helper
                 * @param  {Element} hepler
                 * @param  {Number} startX
                 * @param  {Number} startY
                 * @param  {Number} endX
                 * @param  {Number} endY
                 */
                function moveSelectionHelper(hepler, startX, startY, endX, endY) {

                    var box = transformBox(startX, startY, endX, endY);

                    helper.css({
                        "top": box.beginY + "px",
                        "left": box.beginX + "px",
                        "width": (box.endX - box.beginX) + "px",
                        "height": (box.endY - box.beginY) + "px"
                    });
                }


                /**
                 * Method on Mouse Move
                 * @param  {Event} @event
                 */
                function mousemove(event) {
                    // Prevent default dragging of selected content
                    event.preventDefault();
                    // Move helper
                    moveSelectionHelper(helper, startX, startY, event.pageX, event.pageY);
                    // Check items is selecting
                    var childs = getSelectableElements(element);
                    for (var i = 0; i < childs.length; i++) {
                        if (checkElementHitting(transformBox(offset(childs[i][0]).left, offset(childs[i][0]).top, offset(childs[i][0]).left + childs[i].prop('offsetWidth'), offset(childs[i][0]).top + childs[i].prop('offsetHeight')), transformBox(startX, startY, event.pageX, event.pageY))) {
                            if (childs[i].scope().isSelecting === false) {
                                childs[i].scope().isSelecting = true;
                                childs[i].scope().$apply();
                            }
                        } else {
                            if (childs[i].scope().isSelecting === true) {
                                childs[i].scope().isSelecting = false;
                                childs[i].scope().$apply();
                            }
                        }
                    }
                }



                /**
                 * Event on Mouse up
                 * @param  {Event} event
                 */
                function mouseup(event) {
                    // Prevent default dragging of selected content
                    event.preventDefault();
                    // Remove helper
                    helper.remove();
                    // Change all selecting items to selected
                    var childs = getSelectableElements(element);

                    for (var i = 0; i < childs.length; i++) {
                        if (childs[i].scope().isSelecting === true) {
                            childs[i].scope().isSelecting = false;

                            childs[i].scope().isSelected = event.ctrlKey ? !childs[i].scope().isSelected : true;
                            childs[i].scope().$apply();
                        } else {
                            if (checkElementHitting(transformBox(childs[i].prop('offsetLeft'), childs[i].prop('offsetTop'), childs[i].prop('offsetLeft') + childs[i].prop('offsetWidth'), childs[i].prop('offsetTop') + childs[i].prop('offsetHeight')), transformBox(event.pageX, event.pageY, event.pageX, event.pageY))) {
                                if (childs[i].scope().isSelected === false) {
                                    childs[i].scope().isSelected = true;
                                    childs[i].scope().$apply();
                                }
                            }
                        }
                    }
                    // Remove listeners
                    $document.off('mousemove', mousemove);
                    $document.off('mouseup', mouseup);
                }

                element.on('mousedown', function(event) {
                    // Prevent default dragging of selected content
                    event.preventDefault();
                    if (!event.ctrlKey) {
                        // Skip all selected or selecting items
                        var childs = getSelectableElements(element);
                        for (var i = 0; i < childs.length; i++) {
                            if (childs[i].scope().isSelecting === true || childs[i].scope().isSelected === true) {
                                childs[i].scope().isSelecting = false;
                                childs[i].scope().isSelected = false;
                                childs[i].scope().$apply();
                            }
                        }
                    }
                    // Update start coordinates
                    startX = event.pageX;
                    startY = event.pageY;
                    // Create helper
                    helper = angular
                        .element("<div></div>")
                        .addClass('select-helper');

                    $document.find('body').eq(0).append(helper);
                    // Attach events
                    $document.on('mousemove', mousemove);
                    $document.on('mouseup', mouseup);
                });
            }
        };
    }]);

的style.css

body {margin-top: 30px;}

.blue { background-color: blue; color: white; }

.red { background-color: red; color: white; }

/*=============== UPDATED ===============*/

.item_container {
    border: 1px solid #ccc;
    padding: 10px;
    overflow: hidden;
    /* disable selection */

    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    cursor: crosshair;
}

.item_container div:hover {
    background-color: yellow;
    cursor: pointer;
}

.item_container div.selected:hover {
    background-color: darkgreen;
}

.item_container div {
    width: 50%
}

.select-helper {
    position: absolute;
    border: 1px dashed red;
    background: red;
    opacity: 0.2;
}

.selected {
    background-color: green;
    color: white;
}

.selecting {
    background-color: yellow;
}

0 个答案:

没有答案