AngularJS validate checkbox array

Issue

This might be a duplicate but couldn’t get any useful answer googling myself… if duplicated, sorry. Go haed and delete this post.

I have the following form in AngularJS:

<form novalidate
      class="form-group css-form"
      name="submissionForm"
      ng-submit="submit(submissionForm.$valid)">

    <form-portion content="data.piece1"></form-portion>
    <form-portion content="data.piece2"></form-portion>
    <form-portion content="data.piece3"></form-portion>            
    <form-portion content="data.piece4"></form-portion>
    <form-portion content="data.piece5"></form-portion>
    <form-portion content="data.piece6"></form-portion>
    <form-portion content="data.piece7"></form-portion>
    <form-portion content="data.piece8"></form-portion>
    <form-portion content="data.piece9"></form-portion>

    <div class="row text-center">
        <button type="submit" class="btn btn-success" ng-disabled="submissionForm.$invalid">Submit</button>
        <button type="reset" class="btn btn-warning" ng-click="reset()">Reset</button>
    </div>
</form>

The formPortion directive is defined as follow:

directives.directive('formPortion', function () {
    return {
        restrict: 'AE',
        require: '^form',
        templateUrl: 'partials/form_template.html',
        scope: {
            content: '='
        },
        link: function ($scope) {
            $scope.submissionForm = $scope.$parent.submissionForm;
            $scope.someSelected = function (in_dict) {
                for (var k in in_dict) {
                    if (in_dict[k]) {
                        return true
                    }
                }
                return false
            };
        }
    }
});

The form_template.html looks like:

    <div ng-repeat="(i,row) in content | orderBy : 'order' track by row.field">

        <div ng-switch="row.field1">

            <-- a bunch of other switches -->

            <div ng-switch-when="array">
                <div ng-switch="row.field2">

                    <div ng-switch-when="checkbox" class="row form-group">
                        <div ng-include="'partials/template1.html'"></div>
                    </div>

                    <-- other cases -->
                </div>
            </div> 
        </div>
    </div>

In the template1.html, the checkbox array implementation is defined:

<div class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
    <label>
        {{row.name}}
    </label>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-9" ng-init="row.user_input = {}">
    <div ng-repeat="value in row.allowed_values" class="checkbox-inline">
        <input type="checkbox"
               name="{{row.field}}"
               value="{{value}}"
               ng-model="row.user_input[value]"
               ng-required="{{row.mandatory_field && !someSelected(row.user_input)}}"
        >{{value}}
    </div>
</div>

Thanks to the ng-init directive, it is easy to implement the independent selection of the checkboxes. What I still cannot do is the validation. The ng-required directive get triggered correctly (the ng-required value changes as soon as the one checkbox is clicked) but the submit button does not get activated. This means that the ngModelController.$invalid flag does not get triggered. Here is a snippet of the ngModelController object of my form:

...,
"$error": {
        "required": [{
            "$viewValue": "",
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [null, null],
            "$formatters": [null],
            "$viewChangeListeners": [],
            "$untouched": true,
            "$touched": false,
            "$pristine": false,
            "$dirty": true,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "A",
            "$options": null
        }, {
            "$viewValue": null,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [],
            "$formatters": [],
            "$viewChangeListeners": [],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "B",
            "$options": null
        }, {
            "$viewValue": null,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [],
            "$formatters": [],
            "$viewChangeListeners": [],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "B",
            "$options": null
        }, {
            "$viewValue": null,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [],
            "$formatters": [null],
            "$viewChangeListeners": [],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "C",
            "$options": null
        }, {
            "$viewValue": false,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [null],
            "$formatters": [null],
            "$viewChangeListeners": [null],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "D",
            "$options": null
        }, {
            "$viewValue": false,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [null],
            "$formatters": [null],
            "$viewChangeListeners": [null],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "D",
            "$options": null
        }, {
            "$viewValue": false,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [null],
            "$formatters": [null],
            "$viewChangeListeners": [null],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "D",
            "$options": null
        }, {
            "$viewValue": false,
            "$modelValue": null,
            "$validators": {},
            "$asyncValidators": {},
            "$parsers": [null],
            "$formatters": [null],
            "$viewChangeListeners": [null],
            "$untouched": true,
            "$touched": false,
            "$pristine": true,
            "$dirty": false,
            "$valid": false,
            "$invalid": true,
            "$error": {
                "required": true
            },
            "$name": "D",
            "$options": null
        }, ...

The $name field gets replicated for each checkbox/radio. How can I implement some validation, triggering the general $invalid flag when at least one checkbox is checked?

Solution

After posting another question and surfing the Plunker linked by FelixMelix, I solved my problem. The solution was really stupid. The following line:

ng-required="{{row.mandatory_field && !someSelected(row.user_input)}}"

had to be turned into:

ng-required="row.mandatory_field && !someSelected(row.user_input)"

Answered By – ftabaro

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published