How to show error messages for Checkbox set and Radio set using ng-messages?
Asked Answered
A

2

6

I have been trying to validate Checkbox set and Radio set and show the error messages using ng-messages directive but it does not work out as expected. The ng-messages does not show the error message at all. I am populating error messages using an html file. Up until the errors are resolved, the submit button will be disabled in my form.

How can I show error messages for the Checkbox set and Radio set when:

  1. One option is not selected in Radio set?

  2. At least one option is not selected in Checkbox set?

  3. Scaling the check for the Checkbox set so that we can check at least 2 or more are checked, etc.?

Here's my form:

<form name="userForm" ng-submit="submitForm()" novalidate>
    <div class="form-group" ng-class="{ 'has-error' : userForm.subscribe.$invalid && !userForm.subscribe.$touched }">
        <label class="checkbox-inline">
            <input type="checkbox" id="subscribe1" value="option1" name="subscribe[]" ng-model="user.subscribe" required> 1
        </label>
        <label class="checkbox-inline">
            <input type="checkbox" id="subscribe2" value="option2" name="subscribe[]" ng-model="user.subscribe" required> 2
        </label>
        <label class="checkbox-inline">
            <input type="checkbox" id="subscribe3" value="option3" name="subscribe[]" ng-model="user.subscribe" required> 3
        </label>

        <div class="help-block" ng-messages="userForm.subscribe.$error" ng-show="userForm.subscribe.$invalid">
            <div ng-messages-include="home/messages.html"></div>
        </div>
    </div>

    <div class="form-group" ng-class="{ 'has-error' : userForm.gender.$invalid && !userForm.gender.$touched }">
        <div class="radio">
            <label>
                <input type="radio" name="gender" value="male" ng-model="user.gender" />
                male
            </label>
        </div>
        <div class="radio">
            <label>
                <input type="radio" name="gender" value="female" ng-model="user.gender" />
                female
            </label>
        </div>

        <div class="help-block" ng-messages="userForm.gender.$error" ng-show="userForm.gender.$invalid">
            <div ng-messages-include="home/messages.html"></div>
        </div>
    </div>
    <button type="submit" class="btn btn-primary btn-success " ng-disabled="userForm.$invalid">Submit</button>
</form>

messages.html

<span ng-message="required">This field is required</span>
<span ng-message="minlength">This field is too short</span>
<span ng-message="maxlength">This field is too long</span>
<span ng-message="required">This field is required</span>
<span ng-message="email">This needs to be a valid email</span>

controller.js

angular.module('myApp', ['ngRoute', 'ngAnimate', 'ngMessages']);

angular
    .module('myApp')
    .controller('HomeCtrl', HomeCtrl);

HomeCtrl.$inject = ['$scope'];

function HomeCtrl($scope) {
    $scope.userForm = {};
}

Payment Checkbox set:

<div class="form-group" ng-class="{ 'has-error' : userForm.payment.$invalid && userForm.payment.$touched }">
    <label class="checkbox-inline">
        <input type="checkbox" id="payment1" value="Visa" name="payment" ng-blur="doTouched()" ng-model="user.payment[1]" ng-required="!someSelected(user.payment)"> Visa
    </label>
    <label class="checkbox-inline">
        <input type="checkbox" id="payment2" value="Mastercard" name="payment" ng-blur="doTouched()" ng-model="user.payment[2]" ng-required="!someSelected(user.payment)"> Mastercard
    </label>
    <label class="checkbox-inline">
        <input type="checkbox" id="payment3" value="Cash" name="payment" ng-blur="doTouched()" ng-model="user.payment[3]" ng-required="!someSelected(user.payment)"> Cash
    </label>

    <div class="help-block" ng-messages="userForm.payment.$error" ng-show="userForm.payment.$invalid && userForm.payment.$touched">
        <div ng-messages-include="home/messages.html"></div>
    </div>
</div>
Abisha answered 21/1, 2016 at 7:42 Comment(0)
P
6

Check this sample:

http://plnkr.co/edit/2w0lIf?p=preview

The check boxes list use the ng-required directive with the someSelected function (defined in the controller) which checks if at least one item is selected:

<div class="form-group" ng-class="{ 'has-error' : userForm.subscribe.$invalid && userForm.subscribe.$touched }">
    <label class="checkbox-inline">
        <input type="checkbox" id="subscribe1" value="option1" name="subscribe" ng-blur="doTouched()" ng-model="user.subscribe[1]" ng-required="!someSelected(user.subscribe)"> 1
    </label>
    <label class="checkbox-inline">
        <input type="checkbox" id="subscribe2" value="option2" name="subscribe" ng-blur="doTouched()" ng-model="user.subscribe[2]" ng-required="!someSelected(user.subscribe)"> 2
    </label>
    <label class="checkbox-inline">
        <input type="checkbox" id="subscribe3" value="option3" name="subscribe" ng-blur="doTouched()" ng-model="user.subscribe[3]" ng-required="!someSelected(user.subscribe)"> 3
    </label>

    <div class="help-block" ng-messages="userForm.subscribe.$error" ng-show="userForm.subscribe.$invalid && userForm.subscribe.$touched">
        <div ng-messages-include="messages.html"></div>
    </div>
</div>

The option button group is easier and use the ng-required directive with the condition !user.gender:

<div class="form-group" ng-class="{ 'has-error' : userForm.gender.$invalid && userForm.gender.$touched }">
    <div class="radio">
        <label>
            <input type="radio" name="gender" value="male" ng-model="user.gender" ng-required="!user.gender"/>
            male
        </label>
    </div>
    <div class="radio">
        <label>
            <input type="radio" name="gender" value="female" ng-model="user.gender" ng-required="!user.gender"/>
            female
        </label>
    </div>

    <div class="help-block" ng-messages="userForm.gender.$error" ng-if="userForm.gender.$touched">
        <div ng-messages-include="messages.html"></div>
    </div>
</div>

The ngBlur directive resolves the issue that a check boxes list become "touched" only when all the items in list are blurred, calling doTouched() function::

  $scope.doTouched = function() {
     $scope.userForm.subscribe.$setTouched();
  }

P.S. pay attention to correct names: userForm is the HTML name of the <form>, user is the name of the model to which is bound the form.

Pipistrelle answered 21/1, 2016 at 9:54 Comment(11)
Thanks for the response. I checked out the demo and it seems like that some things are still broken. Like, the button is not disabled & the error messages show up by default, when in fact they should show up only if the are touched. I am trying to fix this. If you know what needs to be fixed, please let me know.Abisha
The problem is that check boxes become "touched" only on blur (focus lost...) and in this case only when all the 3 checkboxes have been blurred userForm.subscribe.$touched becomes truePipistrelle
So I was thinking that if we can somehow check to see if at least one has been touched (as opposed to touching all of them), that should be it?Abisha
I tried to use the code in my app. I see the following error in console: "TypeError: Cannot read property '$setTouched' of undefined". Am I forgetting to do something? Also FYI, I am seeing some console errors when I downloaded your code & ran it.Abisha
Only a little trick to avoid this issue: added if (!object) return false; in the someSelected() function. In fact initially the user.subscribe object is undefined... Plunker updated. However the error didn't invalidate the resultPipistrelle
Sorry to bug you with this. I tried out the revised plunker and it seems like it works correctly on its own. A problem arises if I have another checkbox set in the same form. (I have added the code for the Payment Checkbox set in my posting). The problem is that if you blur on the last option of the 2nd checkbox set, it does show the error for the second checkbox set and at the same time, it shows the error message for the first checkbox set as well, automatically. Although we are using separate IDs etc., this issue persists. I think once we fix this, we should be good to go.Abisha
My answer solve your original answer, what you are asking (new...) is easy to resolve: try to adapt the doTouch() method in order to "touch" the correct set of checkboxes. It's not enough simply copy this method in the new set of fields....Pipistrelle
Thanks a ton for the help so far. Appreciate it very much! For the sake of simplicity, I happened to hardcode the values so that its easy to demonstrate the issue. I get my values from a JSON object, so I cannot hardcode any values. I fetch them dynamically. So I am trying to get your solution working with the existing dynamic format. I shall try again & we'll see how it goes. Thanks again.Abisha
Hope you will be able to reach your goal!Pipistrelle
Hi! If you get a chance, can you please take a look at a new ngMessages related question I posted at: #35138377 ? Thanks.Abisha
@Abisha I put an answer which sets it per checkbox question per page. Its a more simplified version of what I'll probably end up putting in our app (like i have an array of questions that are created via a <question..../> directive and not currentquestion and currentquestion2 vars but I think it'll still apply to that situation as well).Lipson
L
0

Here is an example I created that suites our purpose.
Our app would create multiple checkbox questions per page, and $touched didn't seem to work on input checkbox fields that all had the same name attribute.
Also the custom angular plugins/directives I've seen for checkboxes are nice (where they just bind to 1 array model) but don't seem to support 'required'.

Here i set a custom_touched event per question which I set via ng-click:

http://jsfiddle.net/armyofda12mnkeys/9gLndp5y/7/

<li ng-repeat="choice in currentquestion.choices">
                  <input
                    type="checkbox"
                    ng-model="choice.selected"
                    ng-required="!somethingSelectedInTheGroup(currentquestion.required, currentquestion.choices)"
                    ng-click="currentquestion.custom_touched = true;"
                    name="{{currentquestion.question_code}}"
                    id="{{currentquestion.question_code}}_{{choice.option_value}}"                                      
                    value="{{choice.option_value}}"
                  /> {{choice.orig_option_txt}}

              </li> 


$scope.somethingSelectedInTheGroup = function (is_required, choices) {
    if(is_required) {
      for(var i = 0; i < choices.length; i++) {
        var choice = choices [i];
        if(choice.selected) {
            return true;
        }
      }

      return false;
    } else {
        return false;
    }
  }

Note: i originally had used <div ng-if="questionform.$dirty && questionform[currentquestion2.question_code].$invalid"> but that doesn't work out for multiple checkbox questions per form/page.

Lipson answered 17/3, 2016 at 4:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.