【问题标题】:Prevent repetition in directive attributes?防止指令属性中的重复?
【发布时间】:2014-11-10 07:50:28
【问题描述】:

我有一个指令,它在引导程序 form-group / form-control 中添加一个输入

它监视字段的 $valid$invalid 值,并设置适当的引导错误/有效的 css 类。

这是标记:

<fe-input ng-model='user.first_name' field='first_name' submitted='submitted' label='First Name'></fe-input>
<fe-input ng-model='user.last_name'  field='last_name'  submitted='submitted' label='Last Name'></fe-input>
<fe-input ng-model='user.phone'      field='phone'      submitted='submitted' label='Phone'></fe-input>

它依赖于在用户尝试提交表单时设置的范围变量submitted(因此在用户执行任何操作之前我们不会显示无效字段)

你可以看到有很多重复

  • field = model.field :字段名称始终与模型字段名称相同
  • submitted='submitted' : 每次重复

理想情况下,我想将其缩减为:

<fe-input ng-model='user.first_name' label='First Name'></fe-input>
<fe-input ng-model='user.last_name'  label='Last Name'></fe-input>
<fe-input ng-model='user.phone'      label='Phone'></fe-input>

它将强制执行:

  • 表单字段的名称始终与模型的字段名称相同
  • submitted 在父作用域上是隐式需要的

问题:

  • 这可能吗?
  • 如果是这样,有什么建议可以实现吗?

Plunker:

Here is a plunker 显示我目前拥有的东西。

指令源代码:

这是我的指令的 html 模板:

<div class="form-group" ng-class="{ 'has-success': submitted && isValid,
                                    'has-error'  : submitted && isInvalid }">
    <label class="control-label" for="{{ field }}">
        {{ label }}
    </label>
    <input type="text"
           class="form-control"
           ng-model="model"
           name="{{ field }}"
           id="{{ field }}"
           required>
</div>

和指令来源:

angular.module('directive.form-elements', []).directive('feInput', function() {
    return {
        restrict: 'E',
        require: ['^form'],
        scope: {
            model: '=ngModel',
            label: '@',
            field: '@',
            submitted: '='  // feedback only when the form has been submitted
        },
        templateUrl: 'components/form-elements/input.html',
        replace: true,
        link: function (scope, element, attrs) {
            scope.$parent.$watch('form.'+scope.field+'.$valid', function(isValid) {
                scope.isValid = isValid;
            });
            scope.$parent.$watch('form.'+scope.field+'.$invalid', function(isInvalid) {
                scope.isInvalid = isInvalid;
            });
        }
    };
});

已提交:

submitted 是表单控制器范围内的变量,由表单中的所有输入元素共享。它仅在用户实际尝试提交表单时才启用有效/无效样式

angular.module('myApp').controller('UserCtrl', function($scope) {

    $scope.submitted = false;

    $scope.submit = function(form) {
        $scope.submitted = true;
        if (!form.$valid)
            return;
        // do submit
    });

【问题讨论】:

  • 提交的也用在别处了吗?它是如何设置的?
  • 它在表单控制器的范围内,并在submit 操作中设置。这个想法是所有form-elements 都使用相同的submitted。提交表单时,submitted 设置为 true,因此所有具有$valid=trueform-groups 将具有class="has-success",而具有$invalid=true 的所有class="has-error" 将具有class="has-error"。 Submitted 存在于父级的范围内(即:表单的范围)纯粹是为了打开有效/无效的 css 样式
  • 你把一个小问题过于复杂化了,能不能请你出一个小问题?我对当前代码版本的注释:TRY NEVER TO USE $parent, 不明白为什么 ng-model 和字段都需要,如果它们使用相同的东西,你没有使用$pristine$dirty,为什么?
  • @Linial $dirtysubmitted 不同 - $dirty 是在输入某些/任何内容时。当用户输入电子邮件地址的第一个字母时,向用户显示错误并不是 IMO 的良好用户体验。 field 是表单上的字段。 ng-model 是模型上的值。我相信他们不一样。如果没有使用$parent,你会如何做我所做的事情?
  • @SteveLorimer 嗨,在这个问题上,使用 plunker 进行调试以找到解决方案而不使用 $parent 会更容易。基本上,使用 $parent 对父作用域的状态进行了很大的假设,该作用域现在可以工作,但将来可能会造成麻烦。请添加一个 plunkr,我很高兴看到我们如何使用与 $parent 不同的模式,并使您的指令更短。

标签: angularjs


【解决方案1】:

一种解决方案是创建一个处理重复内容的父指令。 HTML 可能如下所示:

<form name="userForm" fe-form="user">
  <fe-input field='first_name' label='First Name'></fe-input>
  <fe-input field='last_name'  label='Last Name'></fe-input>
  <fe-input field='phone'      label='Phone'></fe-input>
  <button type="submit">Save</button>
<form>

父指令:

angular.module('directive.form-elements', []).directive('feForm', function() {
return {
    controller: function($scope, attrs) {
      var formName = $attrs.name;
      var submitted = false;

      this.hasSuccess = function(field) {
        return submitted && $scope[formName][field].$valid;
      };

      this.hasError = function(field) {
        return submitted && $scope[formName][field].$invalid;
      };

      this.setSubmitted = function(value) {
        submitted = value;
      }
    },
    link: function (scope, element, attrs, controller) {
      controller.model = $scope[$attrs.feForm];
      element.on('submit', function(event) {
          controller.setSubmitted(true);

重要的是它有一个控制器可以被你的其他指令使用。我们还封装了错误和成功状态。

你原来的指令:

angular.module('directive.form-elements', []).directive('feInput', function() {
return {
    restrict: 'E',
    require: ['^form', '^feForm'],
    scope: {
        field: '@',
        label: '@'
    },
    templateUrl: 'components/form-elements/input.html',
    replace: true,
    link: function (scope, element, attrs, controllers) {
        var feFormController = controllers[1];

        scope.model = feFormController.model;
        scope.hasSuccess = feFormController.hasSuccess;
        scope.hasError = feFormController.hasError;
    }
};
});

这里我们可以使用父指令的控制器。

对模板的小修改:

<div class="form-group" ng-class="{ 'has-success':  hasSuccess(field),
                                'has-error'  : hasError(field) }">
<label class="control-label" for="{{ field }}">
    {{ label }}
</label>
<input type="text"
       class="form-control"
       ng-model="model[field]"
       name="{{ field }}"
       id="{{ field }}"
       required>
</div>

请理解,虽然此代码可能对您有用,但它只是用作说明,或者如果您愿意,也可以作为起点。

【讨论】:

  • 感谢您的意见!我已经实现了您的建议 (plunker here)。父控制器的 $scope.model ("userCtrl") 没有链接到 fe-form 指令控制器的范围。你知道为什么吗? TIA!
  • @SteveLorimer 这就是我所说的起点;)您可以为该用例使用观察者,但我决定按照您的示例建立直接绑定&lt;fe-input model='user.first_name' 并派生字段名称。 see updated plunker。顺便说一句:如果您不需要 ngModel 指令,切勿使用 ng-model 作为属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-27
相关资源
最近更新 更多