Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS Directive for twitter bootstrap form-group

I've been playing with angular lately, so far so good, but im struggling with directives.

I'm trying to create a directive that generates the html mark up for a standard bootstrap form group with its corresponding validation messages.

So basically I'm trying to convert this:

<form-group label="Password">
        <input type="password" data-ng-model="vm.password" name="password" id="password" class="form-control form-control-validate"
               required data-ng-minlength="6"
               data-required-error="Password is required" data-minlength-error="Your password must have at least 6 characters" />
</form-group>

into this:

<div class="form-group" data-ng-class="{'has-error': invalid}">
        <label for="password" class="col-md-2 control-label">Password</label>
        <div class="col-md-10">
            <input data-ng-model="vm.password" type="password" id="password" name="password" class="form-control"
                   required data-ng-minlength="6"/>
            <div data-ng-show="changePasswordForm.$dirty && changePasswordForm.oldPassword.$invalid">
                <label data-ng-show="changePasswordForm.oldPassword.$error.required" class="label label-danger">
                    Password is required
                    <br />
                </label>
                <label data-ng-show="changePasswordForm.oldPassword.$error.minlength" class="label label-danger">
                    Your password must have at least 6 characters
                </label>
            </div>
        </div>
</div>

So far this is what I have:

app.directive('formGroup', function () {
    return {
        templateUrl: 'app/directives/formGroup.html',
        restrict: 'E',
        replace: true,
        transclude: true,
        require: "^form",
        scope: {
            label: "@",
        },
        link: function (scope, element, attrs, formController) {
            var input = element.find(":input");
            var id = input.attr("id");
            scope.for = id;
            var inputName = input.attr("name");
            // Build the scope expression that contains the validation status.
            // e.g. "form.example.$invalid"
            var inputInvalid = [formController.$name, inputName, "$invalid"].join(".");
            scope.$parent.$watch(inputInvalid, function (invalid) {
                scope.invalid = invalid;
            });
        }
    };
});

formGroup.html:

<div class="form-group" ng-class="{ 'has-error': invalid }">
   <label class="col-md-2 control-label" for="{{for}}">{{label}}</label>
   <div class="col-md-10">
      <div data-ng-transclude=""></div>
   </div>
</div>

This sets correctly the bootstrap class "has-error" to the form-group if the input is invalid.

Now I want to add validation messages, and I couldn't find a way that works. This is what I have:

app.directive('formControlValidate', function () {
    return {
        templateUrl: "app/directives/formControlValidate.html",
        restrict: 'C',
        require: ["^form", "ngModel"],
        scope: { },
        transclude: true,
        //replace: true,
        link: function (scope, element, attrs, controls) {
            var form = controls[0];
            var inputName = attrs.name;
            var inputErrors = [form.$name, inputName, "$error"].join(".");
            scope.$parent.$watch(inputErrors, function (newValue) {
                if (newValue) {
                    scope.errors = [];
                    angular.forEach(newValue, function (value, key) {
                        var error = attrs[key + 'Error'];
                        if (value && error) {
                            scope.errors.push(error);
                        }
                    });
                }
            }, true);
        }
    };

formControlValidate.html:

<div class="controls" ng-transclude></div>
    <div data-ng-repeat="error in errors">
    <div class="label label-danger">
        {{error}}
    </div>
</div>

But this doesn't work. I'm randomly changing parameters in both directives but can't figure out what how to make it work.

Any ideas or improvements would be greatly appreciated.

Thanks!

like image 206
Diego Avatar asked Mar 24 '14 17:03

Diego


1 Answers

UPDATE: this is my latest gist (angular 1.3): https://gist.github.com/lpsBetty/3259e966947809465cbe


OLD solution:

I tried something similiar, maybe this link can help you too: http://kazimanzurrashid.com/posts/create-angularjs-directives-to-remove-duplicate-codes-in-form

This was my solution. I don't know why but I had to use form.$dirty, it was not possible to use input.$dirty.. (and I use angular-translate)

HTML:

<form-group input="form.password">
  <input type="password" class="form-control" placeholder="{{ 'user.password' | translate }}" required
          name="password" ng-model="user.password" />
</form-group>

Directive:

  app.directive('formGroup', function ($parse) {
    return {
      restrict: 'E',
      require: '^form', 
      transclude: true,
      replace: true,
      scope: {
        cssClass: '@class',
        input: '='
      },
      template: '<div class="form-group" ng-class="{\'has-error\':hasError, cssClass:true}">'+
                  '<div ng-transclude></div>' +
                  '<div ng-show="hasError">' +
                    '<span ng-repeat="(key,error) in input.$error" class="help-block"' +
                            'ng-show="input.$error[key]">{{\'form.invalid.\'+key | translate}}</span>' +
                  '</div>' +
                '</div>',
      link: function (scope, element, attrs, ctrl) {
        var form = ctrl;
        var input = attrs.input;

        scope.$parent.$watch(input+'.$invalid', function (hasError) {
          scope.hasError = hasError && form.$dirty;
        });
      }
    };
  });
like image 158
Betty St Avatar answered Nov 15 '22 05:11

Betty St