From 78f9a1bb0f7ffadcca61111da56fe170f33bf37f Mon Sep 17 00:00:00 2001 From: Chhatoi Pritam Baral Date: Mon, 14 Dec 2015 21:34:27 +0530 Subject: [PATCH 1/2] Fix build and demo --- demo/test.html | 8 ++++---- package.json | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/test.html b/demo/test.html index 2343082..5bf3fd3 100644 --- a/demo/test.html +++ b/demo/test.html @@ -5,8 +5,8 @@ - - + + @@ -37,11 +37,11 @@ group-property="sub" input-model="modernBrowsers" output-model="outputBrowsers" - item-label="{{ icon }} {{ name }}" + item-label="<[ icon ]> <[ name ]>" selection-mode="multi" tick-property="checked" helper-elements="all none reset filter" - button-label="{{ icon }} {{ name }}" + button-label="<[ icon ]> <[ name ]>" button-label-separator='[", ","!?"]' button-template="angular-multi-select-btn-data.htm" > diff --git a/package.json b/package.json index a2679a7..8538935 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "devDependencies": { "bower": "latest", + "grunt": "^0.4.5", "grunt-contrib-uglify": ">=0.5.0", "grunt-contrib-jshint": "0.11.2", "grunt-contrib-cssmin": ">=0.10.0", From dd3587e440d7afa102147c9e0f14e517061bb666 Mon Sep 17 00:00:00 2001 From: Chhatoi Pritam Baral Date: Mon, 14 Dec 2015 22:31:16 +0530 Subject: [PATCH 2/2] Close #1 De-duplicate data that is shown in this widget: this includes the clickable, checkable items and also the button label. This probably needs a refactoring, especially the $scope._walk method --- demo/app.js | 20 +++++++++++-- src/angular-multi-select.js | 56 ++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/demo/app.js b/demo/app.js index 6b28d82..7c8f9d2 100644 --- a/demo/app.js +++ b/demo/app.js @@ -8,16 +8,23 @@ app.controller('MainCtrl', function($scope, $timeout) { sub: [ { icon: '', - name: "Chrome" + name: "Chrome", + engine: "Blink" }, { id: 2, icon: '', - name: "Firefox" + name: "Firefox", + engine: "Gecko" + }, + { + icon: '', + name: "Chrome" }, { icon: '', - name: "Chromium" + name: "Chromium", + engine: "Blink" } ] }, @@ -27,11 +34,18 @@ app.controller('MainCtrl', function($scope, $timeout) { { icon: '', name: "Safari", + engine: "Blink", checked: true }, + { + icon: '', + name: "Chrome", + engine: "Blink" + }, { icon: '', name: "Opera", + engine: "Blink", checked: true } ] diff --git a/src/angular-multi-select.js b/src/angular-multi-select.js index 29f2f4a..847c3cc 100644 --- a/src/angular-multi-select.js +++ b/src/angular-multi-select.js @@ -295,13 +295,15 @@ angular_multi_select.directive('angularMultiSelect', * @returns {*} * @private */ + $scope._walkedPath = []; $scope._walk = function(obj, key, fn) { var _idx; + var fnd = (function(d, o, n) { return fn(o, n, d); }).bind(this, {}); if(angular.isArray(obj)) { var _objs = []; for(_idx in obj) { - var _tmp_obj = $scope._walk(obj[_idx], key, fn); + var _tmp_obj = $scope._walk(obj[_idx], key, fnd); if (_tmp_obj !== null) { _objs.push(_tmp_obj); } @@ -310,17 +312,19 @@ angular_multi_select.directive('angularMultiSelect', return _objs.length > 0 ? _objs : null; } else if(angular.isObject(obj)) { fn = fn || function(){ return true; }; - var should_be_returned = fn(obj); + var should_be_returned = fn(obj, $scope._walkedPath.join('>')); if (obj.hasOwnProperty(key) && angular.isArray(obj[key]) ) { var sub = []; + $scope._walkedPath.push($scope._interpolatedItemLabel(obj)); for(_idx in obj[key]) { - var new_obj = $scope._walk(obj[key][_idx], key, fn); + var new_obj = $scope._walk(obj[key][_idx], key, fnd); if (new_obj !== null) { sub.push(new_obj); } } + $scope._walkedPath.pop(); if(sub.length !== obj[key].length){ obj[key] = sub; @@ -339,22 +343,18 @@ angular_multi_select.directive('angularMultiSelect', * @private */ $scope._syncModels = function(dst, src) { - /* - * We can't use $scope.merge() here as that will wipe - * the elements that are in the src but not in the dst model. - * We need to iterate over the src and dst items at the same - * time and apply changes only when both items exist and the - * tick property is different. - */ - $scope._walk(src, attrs.groupProperty, function(item) { - $scope._walk(dst, attrs.groupProperty, function(_item) { - if(_item[attrs.idProperty] === item[attrs.idProperty]) { - //Don't use extend here as it's really expensive and because - //the only thing that can change in an item is it's tick state. - _item[attrs.tickProperty] = item[attrs.tickProperty]; - } - return true; - }); + var tree = {}; + $scope._walk(src, attrs.groupProperty, function (o, e) { + if (typeof tree[e] === "undefined") tree[e] = {}; + var label = $scope._interpolatedItemLabel(o); + tree[e][label] = o[attrs.tickProperty]; + return true; + }); + $scope._walk(dst, attrs.groupProperty, function (o, e) { + var label = $scope._interpolatedItemLabel(o); + if (tree[e] && o[attrs.tickProperty] !== tree[e][label]) { + o[attrs.tickProperty] = tree[e][label]; + } return true; }); }; @@ -768,6 +768,21 @@ angular_multi_select.directive('angularMultiSelect', }, 0); }; + /** + * De-duplicates entries in a group. Returns true if this object is the first in this group. + * @param {Object} obj + * @param {Array} path + * @param {Object} dupes + * @returns {boolean} + * @private + */ + $scope._dedup = function(obj, path, dupes) { + if (typeof dupes === "undefined") return true; + var label = $scope._interpolatedItemLabel(obj); + if (dupes[label]) return false; + else return (dupes[label] = true); + }; + /** * Returns true if [attrs.searchProperty} matches the search input field (latinized, fuzzy match); * @param {Object} obj @@ -877,6 +892,7 @@ angular_multi_select.directive('angularMultiSelect', */ $scope.fillFilteredModel = function() { $scope.filteredModel = angular.copy($scope._shadowModel); + $scope.filteredModel = $scope._walk($scope.filteredModel, attrs.groupProperty, $scope._dedup); $scope.filteredModel = $scope._walk($scope.filteredModel, attrs.groupProperty, $scope._filter); }; @@ -915,7 +931,7 @@ angular_multi_select.directive('angularMultiSelect', }); _tmp = _tmp === null ? [] : _tmp; - var _new_shadowOutputModel = angular.copy(_tmp); + var _new_shadowOutputModel = $scope._walk(angular.copy(_tmp), attrs.groupProperty, $scope._dedup); if(!$scope.deepCompare(angular.copy($scope._shadowOutputModel), _new_shadowOutputModel)) { $scope._shadowOutputModel = _new_shadowOutputModel; }