简体   繁体   English

具有可选属性的角度指令

[英]Angular Directive with Optional Attributes

I have a custom dropdown directive that has common attributes such as class and ng-model . 我有一个自定义下拉指令,它具有常见的属性,如classng-model

I have decided to extend this control for support for validation and now need to include optional attributes that should only get included in the output template if they are set by the programmer. 我已决定扩展此控件以支持验证,现在需要包含可选属性,如果它们由程序员设置,则只应包含在输出模板中。

Sample 样品

指令调用示例

I have a partially working system in which I moved my code out of a template URL and into a string concatenation which I call in the post: function of the directives compile. 我有一个部分工作的系统,我将代码从模板URL中移出并转换为字符串连接,我在post:函数中指令编译。

I would have preferred to leave my directives HTML in a template, but could not get that working so I have this solution. 我本来希望将我的指令HTML留在模板中,但是无法使其工作,所以我有这个解决方案。

Questions: 问题:

  1. Is this the best way to write a template with dynamic attributes? 这是使用动态属性编写模板的最佳方法吗?
  2. Could this be made to work while leaving the HTML in a template URL 将HTML保留在模板URL中可以使其工作
  3. Should I be using the compile => post function or should this be done in the link function 我应该使用compile => post函数还是应该在link函数中完成

Code for Directive 指令代码

'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));
            }
          }
        }
      };
    }
  ]);

Code for Directive Controller 指令控制器代码

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 used to run the directive 用于运行指令的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>

My Original Template URL content, not used currently 我的原始模板URL内容,当前未使用

<!-- 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>

问题

The "proper" way to introduce a custom input controller is to support the ngModelController . 引入自定义输入控制器的“正确”方法是支持ngModelController This enables your custom control to integrate with other directives that support ngModel , like custom validators, parsers, <form> s. 这使您的自定义控件能够与支持ngModel其他指令集成,如自定义验证器,解析器, <form> This is a bit tricky, but makes your control indistinguishable from built-in controls for the framework: 这有点棘手,但是使您的控件与框架的内置控件无法区分:

.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);
      };
    }
  };
});

Then, it can neatly integrate with other controls and leverage validators likes ng-required natively: 然后,它可以与其他控件整齐地集成,并利用本机ng-required验证器:

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

Demo 演示

It may not seem like the answer to the question you asked, but that is only because your question is a bit of an XY question. 这似乎不是你提出的问题的答案,但这只是因为你的问题是一个XY问题。 By implementing a custom input control, you achieve what you set out to do - assign name attribute to a directive (which registers itself with the form directive, if it is provided) and ng-required works natively. 通过实现自定义输入控件,你实现自己为自己设定的事-指定name属性的指令(其本身注册形式的指令,如果提供的话)和ng-required作品本身。 However, if you must assign name / id to the underlying <select> (for CSS reasons or whatnot), you could use ng-attr- to conditionally apply an attribute. 但是,如果必须name / id分配给基础<select> (出于CSS原因或诸如此类),则可以使用ng-attr-有条件地应用属性。 The template would change to: 模板将更改为:

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

Of course, you'd need to expose attrs on the scope in the link function with: 当然,您需要在链接函数中的范围上公开attrs

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

  // etc...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM