具有可选属性的角度指令

时间:2015-09-09 02:14:38

标签: javascript angularjs angularjs-directive

我有一个自定义下拉指令,它具有常见属性,例如 ng-model

我已决定扩展此控件以支持验证,现在需要包含可选属性,如果它们由程序员设置,则只应包含在输出模板中。

示例

Sample Directive Call

我有一个部分工作的系统,我将代码从模板URL中移出并转换为字符串连接,我在后期调用:指令编译的函数。

我本来希望将我的指令HTML保留在模板中,但是无法正常工作,所以我有了这个解决方案。

问题:

  1. 这是使用动态属性编写模板的最佳方法吗?
  2. 这可以在将HTML保留在模板URL中时使用
  3. 我应该使用compile =>发布功能或应该在链接功能
  4. 中完成

    指令代码

    'use strict';
    
    angular.module(APP)
      .directive('wkKeyLabelSelect', ["$compile",
        function($compile) {
          return {
            restrict: 'EA',
            replace: true,
            scope: {
              'class': '@',              // Permanent - One Way Attribute
              ngModel: '=',              // Permanent - Two Way Attribute (Angular)
              items: '=',                // Permanent - Two Way Attribute (Custom)
              id: '@',                   // Dynamic - One Way Attribute
              name: '@',                 // Dynamic - One Way Attribute
              ngRequired: '=',           // Dynamic - Two Way Attribute (Angular) 
          },
            //templateUrl: COMPONENTS_PATH + '/keyLabelSelect/keyLabelSelect.html',
            controller: 'KeyLabelSelectController',
            link: function (scope, element, attrs) {
              //$compile(element)(scope);
            },
            compile: function (element, attrs) {
    
              // name & ngRequired are not available in the compile scope
              //element.replaceWith($compile(html)(scope));
    
              return {
                pre: function preLink(scope, iElement, iAttrs, controller) {
    
                },
                post: function postLink(scope, iElement, iAttrs, controller) {
    
                  // Template goes here
                  var html =
                    '<select ' +
                      ' class="{{class}}"' +
                      (scope.id ? ' id="{{id}}"' : "") +
                      (scope.name ? ' name="{{name}}"' : "") +
                      (scope.ngRequired ? ' ng-required="true"' : "") +
                      ' ng-model="ngModel"' +
                      ' ng-options="item.key as item.label for item in items"' +
                      '>' +
                    '</select>';
    
                  iElement.replaceWith($compile(html)(scope));
                }
              }
            }
          };
        }
      ]);
    

    指令控制器代码

    angular.module(APP)
    
    .controller('KeyLabelSelectController', ['$scope', function ($scope) {
    
      $scope.klass = typeof $scope.klass === 'undefined' ? 'form-control' : $scope.klass;
    
      console.log($scope.ngModel);
      console.log($scope.items);
    
    }]);
    

    用于运行指令的HTML

    <div class="form-group" ng-class="{ 'has-error': editForm.state.$touched && editForm.name.$invalid }">
        <label class="col-md-3 control-label">State</label>
        <div class="col-md-9">
            <wk-key-label-select id="state" name="state"
                                    ng-required="true"
                                    ng-model="model.entity.state"
                                    class="form-control input-sm"
                                    items="model.lookups.job_state">
            </wk-key-label-select>
    
            <div class="help-block" ng-messages="editForm.state.$error">
                <p ng-message="required">Job State is required.</p>
            </div>
        </div>
    
    </div>
    

    我的原始模板网址内容,目前尚未使用

    <!-- This is now deprecated in place of inline string -->
    <!-- How could I use a in place of string concatenation  -->
    
    <select class="{{klass}}"
            name="{{name}}"
            ng-model="ngModel"
            ng-options="item.key as item.label for item in items"></select>
    

    Questions

1 个答案:

答案 0 :(得分:2)

引入自定义输入控制器的“正确”方法是支持ngModelController。这使您的自定义控件能够与支持ngModel的其他指令集成,例如自定义验证器,解析器,<form>。这有点棘手,但是使您的控件与框架的内置控件无法区分:

.directive("customSelect", function() {
  return {
    require: "?ngModel",
    scope: {
      itemsExp: "&items" // avoids the extra $watcher of "="
    },
    template: '<select ng-model="inner" \
                       ng-options="item.key as item.label for item in itemsExp()"\
                       ng-change="onChange()"></select>',
    link: function(scope, element, attrs, ngModel) {
      if (!ngModel) return;

      // invoked when model changes
      ngModel.$render = function() {
        scope.inner = ngModel.$modelValue;
      };

      scope.onChange = function() {
        ngModel.$setViewValue(scope.inner);
      };
    }
  };
});

然后,它可以与其他控件巧妙地集成,并且本地利用ng-required这样的验证器:

<custom-select name="c1" ng-model="c1" items="items" ng-required="true">
</custom-select>

Demo

这似乎不是你提出的问题的答案,但这只是因为你的问题是一个XY问题。通过实现自定义输入控件,您可以实现您要执行的操作 - 将name属性分配给指令(如果提供了表单指令,则将其自身注册)并且ng-required本机工作。但是,如果您必须name / id分配给基础<select>(出于CSS原因或诸如此类),您可以有条件地使用ng-attr-应用属性。模板将更改为:

<select ng-attr-name="attrs.name || undefined"
        ng-attr-id  ="attrs.id   || undefined"
        ng-model="inner" ...

当然,您需要在链接功能的范围内公开attrs

link: function(scope, element, attrs, ngModel){
  scope.attrs = attrs;

  // etc...
}