I Love Stackoverflow
I Love Stackoverflow

Reputation: 6868

Trying to manipulate array of object

Now in my records :

14.3, 14.2 and 14.1 belongs to part with Id =30.

I am trying to achieve below:

1) By default first 2 ids will be selected.Now if user try to select id = 71 which belongs to part 30 then user should not be allowed to select id=71 because higher version of part 30 is already selected i.e id=76.

2) Now if user uncheck id = 77(33) then user should be allowed to check id=71 because now there is no different part selected so user should be allow to check all part with id = 30 but as soon as user select different part then lower part should be uncheck.

Problem with my code :

1) When I uncheck 16.1 and try to check 14.2 then I am not allowed to check it. I should allow to check 14.2 as now there is no different parts here.

2) 16.1 and 14.3 are check by default. Now when I check 15.1 and then again check 14.1 then 14.3 gets uncheck which is wrong as 14.3 is highest among part id=30 so I should not be able to check 14.1.

  var app = angular.module('myApp', []);
        app.controller('myCtrl', function ($scope) {
            $scope.myArray = [
                 {
                     "id": 77,
                     "selected": true,
                     "part": 33,
                     "name": "16.1", 
                 },
                {
                    "id": 76,
                    "part": 30,
                    "selected": true,
                    "name": "14.3",
                },
                {
                    "id": 71,
                    "part": 30,
                    "selected": false,
                    "name": "14.2",
                },
                {
                    "id": 70,
                    "part": 31,
                    "selected": false,
                    "name": "15.1",
                },
                {
                    "id": 69,
                    "part": 30,
                    "selected": false,
                    "name": "14.1",
                },
                {
                    "id": 68,
                    "part": 29,
                    "selected": false,
                    "name": "13.1",
                },
                 {
                     "id": 55,
                     "part": 26,
                     "selected": false,
                     "name": "12.1",
                 }
                 ,
                 {
                     "id": 54,
                     "part": 25,
                     "selected": false,
                     "name": "11.2",
                 }
                 ,
                 {
                     "id": 53,
                     "part": 25,
                     "selected": false,
                     "name": "11.1",
                 }
            ];

            $scope.checkItem = function (item) {
                if (item.selected) {
                    var index = $scope.myArray.map(m=>m.id).indexOf(item.id);
                    var previousPart = {};
                    for (var i = index - 1; i >= 0; i--) {
                        if ($scope.myArray[i].selected) {
                            previousPart = $scope.myArray[i];
                            break;
                        }
                    }
                    if (item.part != previousPart.part) {
                        for (var i = 0; i < $scope.myArray.length; i++) {
                            if (($scope.myArray[i].part == item.part && $scope.myArray[i].part != item.part)
                                && $scope.myArray[i].selected) {
                                $scope.myArray[i].selected = false;
                                break;
                            }
                        }
                    }
                    else
                        item.selected = false;



                }
            };
        });
<!DOCTYPE html>
<html ng-app="myApp" ng-controller="myCtrl">
<head>
    <title></title>
    <meta charset="utf-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body>
    <div ng-repeat="item in myArray">
        <input ng-model="item.selected" ng-click="checkItem(item)" type="checkbox" />{{ item.name }}
    </div>
</body>
</html>

Upvotes: 14

Views: 455

Answers (2)

Petar Krivoshiev
Petar Krivoshiev

Reputation: 150

I would take a different approach. Separating some of the logic into a service helps me keep the code cleaner. It will be easier to maintain, debug and test. fiddle

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

 app.controller('myCtrl', function($scope, myCollection) {
   $scope.myArray = myCollection.items;

   // Call function every time an item was checked
   $scope.checkItem = function(item) {

     // Remap collection items before and after 
     myCollection.mapCheckedItems();

     // Do something with the user's interaction only if there are
     // different selected parts by definition
     if (item.selected && myCollection.selectedPartsOnly.length > 1) {

       // Iterate through the selected parts
       myCollection.selectedPartsOnly.map(part => {

         // Get the array of items belonging to the exact part number
         var map = myCollection.selectedPartsMap[part];

         for (var j = map.length - 1; j > 0; j--) {
           // By definition deselect all but the highest version.
           // Happens if different part numbers selected simultaneously
           map[j].selected = false;
         }
       })

     }

     // Recalculate collection map to keep it up to date with the
     // items' array
     myCollection.mapCheckedItems();

   };

 });

 app.service('myCollection', function() {
   // Init the collection object
   var self = {};

   // Function to calculate and map items' dependencies
   self.mapCheckedItems = mapCheckedItems;

   // This holds a list of the selected unique part numbers
   // Used to determine needed action easier. By definition if only 
   // one part number is selected and used to iterate through the map
   self.selectedPartsOnly = [];

   // This is the important dictionary where the part numbers is mapped
   // to its child items. Easy access to items grouped by a part number
   self.selectedPartsMap = {};

   // The actual array definition
   self.items = [{
     "id": 77,
     "selected": true,
     "part": 33,
     "name": "16.1",
   }, {
     "id": 76,
     "part": 30,
     "selected": true,
     "name": "14.3",
   }, {
     "id": 71,
     "part": 30,
     "selected": false,
     "name": "14.2",
   }, {
     "id": 70,
     "part": 31,
     "selected": false,
     "name": "15.1",
   }, {
     "id": 69,
     "part": 30,
     "selected": false,
     "name": "14.1",
   }, {
     "id": 68,
     "part": 29,
     "selected": false,
     "name": "13.1",
   }, {
     "id": 55,
     "part": 26,
     "selected": false,
     "name": "12.1",
   }, {
     "id": 54,
     "part": 25,
     "selected": false,
     "name": "11.2",
   }, {
     "id": 53,
     "part": 25,
     "selected": false,
     "name": "11.1",
   }];

   // Init the helpers once on start. This will be executed only once
   mapCheckedItems();

   // Return the service object to be accessed from the controller
   return self;

   // This function will create and update the objects mapping
   function mapCheckedItems() {

     // Reset the helpers
     self.selectedPartsOnly = [];
     self.selectedPartsMap = {};
     
     // Now we iterate through the selected items.         
     self.items
       .filter(item => item.selected)
       .map(item => {

         // Map every selected item directly to one part number
         mapSelectedParts(item);

         // Determine what part numbers are in use.
         mapSelectedPartsOnly(item.part);
       })
   }

   function mapSelectedPartsOnly(part) {
     if (self.selectedPartsOnly.indexOf(part) == -1)
       self.selectedPartsOnly.push(part);
   }

   function mapSelectedParts(item) {
     if (!self.selectedPartsMap[item.part])
       self.selectedPartsMap[item.part] = [];

     self.selectedPartsMap[item.part].push(item);
   }

 })
<!DOCTYPE html>
<html ng-app="myApp" ng-controller="myCtrl">
<head>
    <title></title>
    <meta charset="utf-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
</head>
<body>
    <div ng-repeat="item in myArray">
        <input ng-model="item.selected" ng-click="checkItem(item)" type="checkbox" />{{ item.name }}
    </div>
</body>
</html>

Upvotes: 3

Gaurav Kumar Singh
Gaurav Kumar Singh

Reputation: 1570

I've created a solution which will achieve your both points and resolve your problems as mentioned above.

You need to update checkItem function in controller only

$scope.checkItem = function(item) {
  if (item.selected) {
    var otherExists = $scope.myArray.filter(function(m) {
      return ((m.part != item.part) && m.selected);
    }).length;
    var selfExists = $scope.myArray.filter(function(m) {
      return ((m.part == item.part) && m.selected);
    }).length - 1;
    if (!!otherExists) {
      if (!!selfExists) {
        var selectedIsLower = $scope.myArray.filter(function(m) {
          return ((m.part == item.part) && (m.id > item.id) && m.selected);
        }).length;
        if (!!selectedIsLower) {
          item.selected = false;
        } else {
          $scope.myArray.filter(function(m) {
            return (m.part == item.part);
          }).forEach(function(m) {
            m.selected = false;
          });
          item.selected = true;
        }
      } else {
        item.selected = false;
        var allSelectedIds = [];
        var allSelected = $scope.myArray.filter(function(m) {
          return m.selected;
        });
        allSelected.map(m => m.part).forEach(function(a) {
          if (allSelectedIds.indexOf(a) < 0) { //skip duplicate id
            allSelectedIds.push(a);
          }
        });;
        if (allSelectedIds.length == 1) {
          var maxEle = {};
          allSelected.forEach(function(m, count) {
            if (maxEle.id) {
              (maxEle.id > m.id) && (m.selected = false);
            }
            maxEle.id = m.id;
            maxEle.count = count;
          });
        }
        item.selected = true;
      }
    }
  }
};

Working fiddle

Upvotes: 1

Related Questions