【问题标题】:Angular Directive with Optional Attributes具有可选属性的 Angular 指令
【发布时间】:2015-09-09 02:14:38
【问题描述】:

我有一个自定义下拉指令,它具有 classng-model 等常用属性。

我决定扩展此控件以支持验证,现在需要包含可选属性,这些属性只有在程序员设置的情况下才应包含在输出模板中。

示例

我有一个部分工作的系统,在该系统中我将代码从模板 URL 中移到了我在帖子中调用的字符串连接中:指令的函数编译。

我宁愿将指令 HTML 留在模板中,但无法使其正常工作,所以我有这个解决方案。

问题:

  1. 这是编写具有动态属性的模板的最佳方式吗?
  2. 是否可以在将 HTML 保留在模板 URL 中时使其工作
  3. 我应该使用 compile => post 函数还是应该在链接函数中完成此操作

指令代码

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

我的原始模板 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>

【问题讨论】:

  • 为什么会有和你粘贴的代码一模一样的代码截图?此外,您似乎正在实施custom input control,但您应该正确使用ngModelController - 执行scope: {ngModel: "="} 是错误的方法。
  • 图片标注了具体问题,指向有问题的代码。这就是为什么它看起来像重复的原因,我有代码和带注释的图像
  • 我不知道 ngModelController,现在正在研究它
  • 针对“应该是peroperly使用ngModelController”,我做了一些研究,因为我没有做自定义验证或复杂的属性,没有必要介绍使用ngModelController的复杂性。我的意见来源是:radify.io/blog/…bennadel.com/blog/…
  • 您正在引入自定义输入控件。当然,它在其模板中使用现有的输入控件,但如果您希望它支持 ngModel 模型(即支持其他验证器、自定义解析器、与 &lt;form&gt; 集成等),那么使用 @ 是个好主意987654336@。您不必必须,但这是最有角度的方式。总的来说,您是在问一个 XY 问题 - 相反,请解释您正在尝试使用普通 &lt;select&gt; 无法提供的自定义下拉菜单实现的目标

标签: javascript angularjs angularjs-directive


【解决方案1】:

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

.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 分配给底层&lt;select&gt;(出于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...
}

【讨论】:

  • 在 ng-attr 上的第二秒是一个真正的帮助,我在这里阅读了它:thinkingmedia.ca/2015/03/…
  • 大多数情况下,我在可选属性方面遇到了麻烦(
  • @DavidCruwys,可选属性有什么问题?
猜你喜欢
  • 2015-10-15
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-15
相关资源
最近更新 更多