我目前正在使用这个名为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;
}