olingern
olingern

Reputation: 493

AngularJS dynamic required attribute in directive and form validation

I have a directive that receives whether an element should be required or not from a REST api. Right now, I can't seem to get the form to invalidate when an attribute is set to required.

So, in essence I'm able to dynamically add the 'required' attribute from the directive below, but it doesn't invalidate the form. Looking through chrome I see that, even though the required attribute exists, a required entry in the $error array doesn't exist.

app.directive('requireiftrue', function ($compile) {
    return {
        require: '?ngModel',
        link: function (scope, el, attrs, ngModel) {
            if (!ngModel) {
                return;
            }

            if(attrs.requireiftrue==="true"){
                console.log('should require');
                el.attr('required', true);
                $compile(el.contents())(scope);
            }
            else{
                console.log('should not require');   
            }
        }
    };
});

Here's a jsfiddle to illustrate the problem. And, here's sample JSON returned from my rest API

{
    race: false,
    martialStatus: true,
}

EDIT: While the accepted answer got me up and running, I still had a good bit of finagling to do.

Namely: 1. Resolving a deferred promise to ensure that my form actually receives the required fields to validate 2. observing my 'requireiftrue' attribute

My solution

module config:

function config($stateProvider) {
    $stateProvider

    .state('testState', {
        url: '/test/form',
        controller: 'TestCtrl',
        templateUrl: 'test/form/testForm.tpl.html',
        resolve: {
            formDefaultService: function getFormDefaults($q, dataservice) {
                // Set up a promise to return
                var deferred = $q.defer();
                var myData = dataservice.getFormDefaults();
                deferred.resolve(myData);

                return deferred.promise;
                //return 
            }
        },

        data: {
            pageTitle: 'Test Info'
        }
    });
}

And, finally the directive / HTML that receives api data:

Directive:

.directive('requireiftrue', function ($compile) {
    return {
        require: '?ngModel',
        link: function (scope, el, attrs, ngModel) {
            if (!ngModel) {
                return;
            }
            attrs.$observe('requireiftrue', function(value){ 
                if(value==="true"){
                    el.attr('required', true);
                    el.removeAttr('requireiftrue');
                    $compile(el[0])(scope);
                }
            });
        }
    };
});

HTML:

<input max="40"
requireiftrue={{defaults.validation.name}}
validNumber
id="name"
name="name"
type="text"
ng-model="test.name"
class="form-control">

Upvotes: 4

Views: 21955

Answers (5)

Rajeshwar
Rajeshwar

Reputation: 2509

Instead of using a directive, use ng-init to initialize requireiftrue. and assign this value to ng-required like ng-required="requireiftrue" as shown below. As you said you are getting the data from rest api, you can initialize requireiftrue with the value you are getting from api, instead of true or false as shown in example below.

Hope this helps you.

Updated fiddle http://jsfiddle.net/zsrfe513/3/

<form ng-app="form-example" name='fo' class="row form-horizontal" novalidate>
<div class="control-group" ng-form="testReq">
    <h3>Form invalid: {{testReq.$invalid}}</h3>
 <label class="control-label" for="inputEmail">Email</label>
 <div class="controls" ng-init='requireiftrue = true'>
   <input id="inputEmail" placeholder="Email" ng-model="email"  name='ip' ng-required='requireiftrue'>

    <span ng-show="testReq.ip.$dirty && testReq.ip.$error.required">
        Required.
    </span>
   </div>
 </div>
</form>

Upvotes: 4

Richard Torcato
Richard Torcato

Reputation: 2762

I used <input ng-required="true"> worked fine for my angular validation component.

If your using the new angular component make sure to pass in required: "@" instead of required: "="

scope: {
      required: '@'
    }

I also took this further and required integer and min/max validation the same way.

Upvotes: 0

Support JDWebapps
Support JDWebapps

Reputation: 19

You just need to add the "required" attribute to the input.

<input max="40"
requireiftrue={{defaults.validation.name}}
validNumber
id="name"
name="name"
type="text"
ng-model="test.name"
class="form-control"
required="required">

Upvotes: 0

Aviv Shaked
Aviv Shaked

Reputation: 762

You had two issues: The first is el.contents() returned an empty array. so The first thing you should do is change it to el[0]. But had el.contents() worked you would hav had a much more serious problem. You would have been trying to compile a directive that has itself as a directive which would lead to an infinite loop (well until the browser crashed any way). So here is the revised code:

var app = angular.module('form-example', []);

app.directive('requireiftrue', function ($compile) {
    return {
        require: '?ngModel',
        link: function (scope, el, attrs, ngModel) {
            if (!ngModel) {
                return;
            }

            if(attrs.requireiftrue==="true"){
                console.log('should require');
                el.attr('required', true);
                el.removeAttr('requireiftrue');
                $compile(el[0])(scope);
            }
            else{
                console.log('should not require');   
            }
        }
    };
});

I should note however that now this directive is a one-off. If the model will change, the directive will not be on the element any longer to deal with it.

Upvotes: 5

Michael Coleman
Michael Coleman

Reputation: 3398

Try:

1. adding the required directive to the input you want to apply validation to

<input id="inputEmail" class="requireiftrue" placeholder="Email" 
    ng-model="email" requireiftrue="true" required>
2 Defining the directive as type class and adding the directive class to the HTML input field

JS

app.directive('requireiftrue', function ($compile) {
   return {
    restrict: 'C',
    require: '?ngModel',
    .....

HTML

<input id="inputEmail" class="requireiftrue" placeholder="Email" ng-model="email" requireiftrue="true" required>

here is a update of your fiddle - http://jsfiddle.net/4fb6wg30/

Upvotes: 1

Related Questions