SoluableNonagon SoluableNonagon - 6 months ago 15
AngularJS Question

AngularJS data binding checkboxes to object if checked

I have a JSON object which I am repeating with ng-repeat, the keys are strings, and the values are an array. I am listing each of the values as a checkbox. I would like to create a second object which contains a list of only the checkboxes that are checked. I want to preserve the structure of the object with keys and values.

I'm unsure how to bind this to a model properly so that the structure is preserved.

http://jsfiddle.net/NDFc2/3/

This is my HTML

<h3 >Dynamic data binding in AngularJS</h3>
<div ng-app ng-controller="Controller" class="container">
<h4>Inputs</h4>
<ul ng-repeat="(parent, values) in inputs">
<span>{{parent}} : </span>
<li ng-repeat="value in values"><label>{{value}}
<input type="checkbox" ng-model="output[parent]" ng-checked="output[parent]" value="value" >
</input></label>
</li>
</ul>

<h4>Outputs</h4>
<ul ng-repeat="(key,value) in inputs">
<li>
{{key}} : {{output[key]}}
</li>
</ul>
</div>


and my JS

function Controller($scope) {
$scope.output = {};
$scope.inputs = {'category': ['one','two','three'], 'color':['blue','green']};
}


Is there some simple way to do this? I have the feeling that I'm missing something minor and this will all work nicely.

m59 m59
Answer

My examples have your angular logic in the recommended syntax (non-global). There were also several issues with your markup that I have corrected.

In this example, ng-model="x" is a placeholder that I don't use, but ng-model must be present or an error is thrown. I am using ng-change to handle the link between the checkboxes and $scope.outputs.

Live demo here (click).

Markup:

<div ng-app="myApp" ng-controller="myCtrl">
  <h3 >Dynamic data binding AngularJS</h3>
  <h4>Inputs</h4>
  <ul>
    <li ng-repeat="(typeKey, typeVal) in inputs">
      <span>{{typeKey}} : </span>
      <ul>
        <li ng-repeat="value in typeVal">
          <label>{{value}}
            <input
              type="checkbox"
              ng-model="x"
              ng-change="setOutput(typeKey, $index, value)"
            >
          </label>
        </li>
      </ul>
    </li>
  </ul>    

  <h4>Outputs</h4>
  <ul ng-repeat="(key,value) in inputs">
    <li>{{key}} : {{outputs[key]}}</li>
  </ul>
</div>

JavaScript:

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

app.controller('myCtrl', function($scope) {
  $scope.outputs = {};
  $scope.inputs = {
    'category': ['one','two','three'],
    'color':['blue','green']
  };
  $scope.setOutput = function(typeKey, $index, value) {
    $scope.outputs[typeKey] = $scope.outputs[typeKey] || [];
    $scope.outputs[typeKey][$index] = value;
  };
});

Another Solution

Live demo here (click).

First, I used ng-init to dynamically add the first-level properties from inputs to outputs. Then you just needed to set your ng-model and ng-checked properties to the correct location in outputs.

Markup:

<div ng-app="myApp" ng-controller="myCtrl">
  <h3 >Dynamic data binding AngularJS</h3>
  <h4>Inputs</h4>
  <ul>
    <li 
      ng-repeat="(typeKey, typeVal) in inputs"
      ng-init="outputs[typeKey] = outputs[typeKey] || {}">
      <span>{{typeKey}} : </span>
      <ul>
        <li ng-repeat="value in typeVal">
          <label>{{value}}
            <input
              type="checkbox"
              ng-model="outputs[typeKey][value]"
              ng-checked="outputs[typeKey][value]"
              value="outputs[typeKey][value]"
            >
          </label>
        </li>
      </ul>
    </li>
  </ul>    

  <h4>Outputs</h4>
  <ul ng-repeat="(key,value) in inputs">
    <li>{{key}} : {{outputs[key]}}</li>
  </ul>
</div>

JavaScript:

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

app.controller('myCtrl', function($scope) {
  $scope.outputs = {};
  $scope.inputs = {
    'category': ['one','two','three'],
    'color':['blue','green']
  };
});