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

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

```lang: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>
```

```lang: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'
    };
});


```
```lang: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>
```

こんな感じになります。

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

※注意点２
動的に生成したinputのNgModelControllerが、FormController内にinputの`name`のvalueをキーにして保持されるのを想定したのですが、template内に記述した`{{id}}`をキーとしてしまっているため、nameごとに参照できなくなっています。
![スクリーンショット 2015-02-20 19.00.17.png](https://qiita-image-store.s3.amazonaws.com/0/70000/78b3b97b-2a6d-53ad-f6d3-498af1afea11.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](https://qiita-image-store.s3.amazonaws.com/0/70000/415d922a-5453-87cb-fbfa-cfbbb2ba2630.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から正規表現オブジェクトに変わっているところにも注意）