LoginSignup
3
3

More than 5 years have passed since last update.

angular1.2系でinputを生成するdirectiveを作る時の注意点

Posted at

入力項目が大量にあるようなフォームをangularJSで作る場合に、ラベル・テキストinput・validationエラー表示などをひとまとめにしたdirectiveを作ると、UIが変わったりしても全体に一気に反映できるので便利です。

html
<div ng-form name='testForm'>
    <div my-input id="name" ng-model="formData.name" ng-required="true" placeholder="山田太郎" title="氏名"></div>
    <div my-input id="zip-code" ng-model="formData.zipCode" ng-required="true" placeholder="1234567" title="郵便番号" validation="patterns.zipCode"></div>
    <div my-input id="phone-number" ng-model="formData.phoneNumber" ng-required="true" placeholder="0312345678" title="電話番号" validation="patterns.phone"></div>
    <button class="button btn btn-primary btn-block" ng-disabled="testForm.$invalid">送信</button>
</div>
js
angular.module('TestApp', [])
.controller('myCtrl', ['$scope', function ($scope) {
    //validationまわりをエラー文言と一緒にまとめて定義しています
    $scope.patterns = {
        zipCode: {
            regexp: '/^[0-9]{7}$/',
            msg: 'ハイフンなしの7桁の数字で入力してください',
            maxlength: 7
        },
        phone: {
            regexp: '/^[0-9]{10,11}$/',
            msg: 'ハイフンなしの半角数字を10〜11桁で入力してください',
            maxlength: 11
        }
    };
}])
.directive('myInput', function () {
    return {
        restrict: 'A',
        require: ['^?form'],
        transclude: true,
        replace: true,
        scope: {
            title: '@',
            id: '@',
            ngModel: '=',
            ngRequired: '=',
            validation: '=',
            placeholder: '@?',
            errMsg: '=?'
        },
        controller: ['$scope', function ($scope) {
            // create error msg
            if (!$scope.errMsg) {
                $scope.errMsg = {};
            }
            if ($scope.ngRequired === true && !('required' in $scope.errMsg)) {
                $scope.errMsg.required = $scope.title + 'を入力して下さい';
            }
            if ($scope.validation && !('invalid' in $scope.errMsg)) {
                $scope.errMsg.invalid = $scope.validation.msg;
            }
        }],
        link: function (scope, element, attrs, ctrl) {
            scope.target = ctrl[0][attrs.id];
        },
        templateUrl: 'my-input.html'
    };
});


directiveのtemplateとなるmy-input.html
<div class="field" class="form-group" ng-class="{'has-error': target.$dirty && target.$invalid}">
   <label ng-bind="title" class="control-label"></label><input name="{{id}}" ng-model="ngModel" ng-required="ngRequired" ng-pattern="{{validation.regexp}}" maxlength="{{validation.maxlength}}" placeholder="{{placeholder}}" type="text" class="form-control" />
   <div class="errors" ng-show="target.$dirty">
      <p ng-bind="errMsg.required" ng-if="target.$error.required && errMsg.required"></p>
      <p ng-bind="errMsg.invalid" ng-if="!target.$error.required && target.$invalid && errMsg.invalid"></p>
   </div>
</div>

こんな感じになります。

※注意点1
idというattributeをinputのnameに引き継いでいますが、このattribute名をinputに合わせてnameにしてしまうと、ngModelの$errorの内容がうまくupdateされません。

※注意点2
動的に生成したinputのNgModelControllerが、FormController内にinputのnameのvalueをキーにして保持されるのを想定したのですが、template内に記述した{{id}}をキーとしてしまっているため、nameごとに参照できなくなっています。
スクリーンショット 2015-02-20 19.00.17.png

この問題は
https://github.com/angular/angular.js/issues/1404
にissueとして上がっています。

この回避策
https://github.com/angular/angular.js/issues/1404#issuecomment-30859987
によって期待通りの動きになります。

スクリーンショット 2015-02-20 19.02.02.png

なお、どちらの問題も、angularを1.3.5にアップデートしたところ、修正されているようでした。:smile:

以下は1.2系と1.3系での動作サンプルになります。

angular 1.2.16
http://jsfiddle.net/miyukiw/m03f2ymt/4/

angular 1.3.5
http://jsfiddle.net/miyukiw/9d64oa1m/5/

(※1.2と1.3ではng-patternのvalueがstringから正規表現オブジェクトに変わっているところにも注意)

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3