2

I have a view model containg an object that is used to display some checkboxes:

components =  {
  "ComponentInfos": [
    {
      "Id": "1abb0ee5-7e44-4e45-92da-150079066e99",
      "FriendlyName": "Component1",
      "LimitInfos": [
        {
          "Id": "4b7cd37a-2378-4f4f-921b-e0375d60d19c",
          "FriendlyName": "Component1 Full",
        },
        {
          "Id": "ff9ebe78-fbe4-4a26-a3df-6ec8e52cd0f2",
          "FriendlyName": "Component1 Light",
        }
      ]
    }

I am able to create the checkboxes with FriendlyName as label:

<h4>{{l.FriendlyName}}</h4>

<div>
    <div ng-repeat="limitinfo in l.LimitInfos">
        <label>
            <input type="checkbox" ng-model="vm.settings.ComponentInfos[limitinfo.Id]" 
            value="{{limitinfo.Id}}"/> {{limitinfo.FriendlyName}}
        </label>
    </div>
</div>

I want to store the selected LimitInfo.Id in an array for each selected checkbox. I was able to store them in an object like this:

settings = {
    "ComponentInfos" : {}
  }; 

Result example:

"2e80bedb-4a18-4cc4-bdfd-837ffa130947": true,
"add1edf8-4f11-4178-9c78-d591a6f590e3": true

What I do need is to store the LimitInfo.Idin an array like this:

settings = {
    "ComponentInfos" : []
  };

Expected result:

"2e80bedb-4a18-4cc4-bdfd-837ffa130947", "add1edf8-4f11-4178-9c78-d591a6f590e3"

I uploaded my code to Plunker.

Martin Brandl
  • 56,134
  • 13
  • 133
  • 172

5 Answers5

3

One line solution

You can do the following in vanilla JS (ES5 and above, so modern browsers)

var data = {
    "a": true,
    "b": false,
    "c": true,
    "d": false,
    "e": true,
    "f": true
  }

var arr = Object.keys(data).filter( key => !!data[key] );

// ['a', 'c', 'e', 'f']
amd
  • 20,637
  • 6
  • 49
  • 67
3

you can use a ng-click method on the checkbox with a custom controller method to push to that array.

<input type="checkbox" ng-model="vm.settings.ComponentInfos[limitinfo.Id]" 
                value="{{limitinfo.Id}}" ng-click="toggleSelection(limitinfo.ImpliedLimits)"/> 



$scope.toggleSelection = function toggleSelection(item) {
    var idx = $scope.vm.settings.ComponentInfos.indexOf(item);
    if (idx > -1) {
      $scope.vm.settings.ComponentInfos.splice(idx, 1);
    }
    else {
      $scope.vm.settings.ComponentInfos.push(item[0]);
    }
};

see this plnkr.

see this answer

Community
  • 1
  • 1
Nitsan Baleli
  • 5,393
  • 3
  • 30
  • 52
  • This looks like a good solution, unfortunately the unchecked components where not removed but added again and it seems like some checkboxes have the same id or null – Martin Brandl May 30 '16 at 07:09
  • if you need to handle cases where items are not unique you should add code on top of my answer, or maybe consider storing them in an object. cheers – Nitsan Baleli May 30 '16 at 07:40
  • Ahh, I figured it out. I forgot to remove the `ImpliedLimits` property which you are using within your example - sorry, my fault. Im struggling now to get the saved checkboxes checked after I retrieve the saved settings. Here is the updated plnkr: https://plnkr.co/edit/UaI1F1lHtal3TezlEPIV?p=preview – Martin Brandl May 30 '16 at 08:20
  • great. what do you mean retreive saved settings? – Nitsan Baleli May 30 '16 at 08:40
2

You can use the ng-click and make an update of your list. I've added this to your MyViewModel function and changed the type of your ComponentInfosto an array.

this.update = function (value) {
    var exists = false;
    for (var elem of this.settings["ComponentInfos"]){
      if (elem === value) {
        exists = true;
      }
    }

    if(exists) {
      var index = this.settings["ComponentInfos"].indexOf(value);
      this.settings["ComponentInfos"].splice(index,1);
    } else {
      this.settings["ComponentInfos"].push(value);
    }
  }

Additionally you need to change the input in the html to

<input type="checkbox" ng-click="vm.update(limitinfo.Id)"/> {{limitinfo.FriendlyName}}

Massimo Rosin
  • 163
  • 1
  • 13
2

Demo by directive:

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

app.directive('myCheckbox',function(){
  return {
    restrict:'EA',
    template:'<label>'
                +'<input type="checkbox" ng-model="model" ng-change="toggleModel()" /> {{label}}'
            +'</label>',
    replace: true,
    scope:{
      label:'@',
      value:'@',
      output:'='
    },
    link:function(scope,elements,attrs){

      //init checked status
      scope.model=scope.output.indexOf(scope.value) > -1;
      
      //binding click replace watch model
      scope.toggleModel = function(){
        if(scope.model){
          scope.output.push(scope.value);
          return false;
        }
        scope.output.splice(scope.output.indexOf(scope.value),1);
      }
      
    }
  }
  
});

function MyViewModel()
{
  this.components =  {
  "ComponentInfos": [
    {
      "Id": "1abb0ee5-7e44-4e45-92da-150079066e99",
      "FriendlyName": "Component1",
      "LimitInfos": [
        {
          "Id": "4b7cd37a-2378-4f4f-921b-e0375d60d19c",
          "FriendlyName": "Component1 Full",
          "ImpliedLimits": [
            "ff9ebe78-fbe4-4a26-a3df-6ec8e52cd0f2"
          ]
        },
        {
          "Id": "ff9ebe78-fbe4-4a26-a3df-6ec8e52cd0f2",
          "FriendlyName": "Component1 Light",
          "ImpliedLimits": [
            "4f74abce-5da5-4740-bf89-dc47dafe6c5f"
          ]
        },
        {
          "Id": "4f74abce-5da5-4740-bf89-dc47dafe6c5f",
          "FriendlyName": "Component2 User",
          "ImpliedLimits": []
        }
      ]
    },
    {
      "Id": "ad95e191-26ee-447a-866a-920695bb3ab6",
      "FriendlyName": "Component2",
      "LimitInfos": [
        {
          "Id": "8d13765a-978e-4d12-a1aa-24a1dda2149b",
          "FriendlyName": "Component2 Full",
          "ImpliedLimits": [
            "4f74abce-5da5-4740-bf89-dc47dafe6c5f"
          ]
        },
        {
          "Id": "2e80bedb-4a18-4cc4-bdfd-837ffa130947",
          "FriendlyName": "Component2 Light",
          "ImpliedLimits": [
            "4f74abce-5da5-4740-bf89-dc47dafe6c5f"
          ]
        },
        {
          "Id": "add1edf8-4f11-4178-9c78-d591a6f590e3",
          "FriendlyName": "Component2 Viewer",
          "ImpliedLimits": [
            "4f74abce-5da5-4740-bf89-dc47dafe6c5f"
          ]
        }
      ]
    }
  ]
};

  this.settings = {
    "ComponentInfos" : ["4b7cd37a-2378-4f4f-921b-e0375d60d19c","2e80bedb-4a18-4cc4-bdfd-837ffa130947"]
  };
}



app.controller('MainCtrl', function($scope) {
  $scope.vm = new MyViewModel();
});
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.20/angular.js" data-semver="1.3.20"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <div ng-repeat="l in vm.components.ComponentInfos">

    <h4>{{l.FriendlyName}}</h4>

    <div>
        <div ng-repeat="limitinfo in l.LimitInfos">
            <my-checkbox label="{{limitinfo.FriendlyName}}" value="{{limitinfo.Id}}" output="vm.settings.ComponentInfos"></my-checkbox>
        </div>
    </div>
  </div>
  
  
  <hr>
  <pre>
    {{vm.settings | json }}
  </pre>
  
  </body>

</html>
Yin Gang
  • 1,443
  • 1
  • 10
  • 15
  • Working like a charm. I try to get a solution without a directive (looks like a bit overkill) but this will be my fallback ;-). Thank you – Martin Brandl May 30 '16 at 07:24
  • Looking forward to your solution, please share to us, thx. :) – Yin Gang May 30 '16 at 07:44
  • Is there any way to get the view updated when I load the `ComponentInfos` array? For example, when you enter `"ComponentInfos": [ "add1edf8-4f11-4178-9c78-d591a6f590e3" ]` to get the checkbox checked? – Martin Brandl May 30 '16 at 07:57
  • Somehow, if i click directly on the checkbox, the id is successfully added to the array but the checkbox doesn't get checked. If I click on the label, the checkbox gets checked. Can you help me? – Martin Brandl May 30 '16 at 08:59
  • 1
    My apoligize, have updated the [demo](https://plnkr.co/edit/0MInSD?p=preview) again, and the snippet above, should handle user action by `onchange` on input rather than `onclick` on label. – Yin Gang May 30 '16 at 09:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/113302/discussion-between-jisaak-and-yin-gang). – Martin Brandl May 30 '16 at 09:51
1

I am new to angularJS, Try this :

Add this snippet to app.js

this.data =[];
this.selection = function(){
    this.data =[];
    angular.forEach(this.settings["ComponentInfos"], function(value, key) {
      if(value)
        this.push(key);
    }, this.data);
 }

This to body of index.html

<div ng-repeat="l in vm.components.ComponentInfos">

<h4>{{l.FriendlyName}}</h4>

<div>
    <div ng-repeat="limitinfo in l.LimitInfos">
        <label>
            <input type="checkbox" ng-model="vm.settings.ComponentInfos[limitinfo.Id]" ng-click="vm.selection()"
            value="{{limitinfo.Id}}"/> {{limitinfo.FriendlyName}}
        </label>
    </div>
</div>
</div>
<hr>
<pre>
 {{vm.settings | json }}
 {{vm.data}}
</pre>
Sabith
  • 1,628
  • 2
  • 20
  • 38