【问题标题】:Ng-messages dynamic form and input namesNg-messages 动态表单和输入名称
【发布时间】:2017-01-18 10:22:04
【问题描述】:

tl;博士;

似乎无法嵌入 like ng-messages 指令。当您嵌入它时,它无法正确绑定。我有 2 个解决方法,但都有其缺点。有人有更好的解决方案还是我做错了什么?

我正在使用打字稿。


版本

AngularJS:1.6.1

角度动画:1.6.1

Angular-aria:1.6.1

Angular 消息:1.6.1

角材料:1.1.0

TypeScript:3.10.5


我想要什么

我有一个组件可以格式化日期(还有更多功能,但不相关)并显示它。其中一些组件包含在同一页面上。围绕这些组件的是一种形式。我想使用自定义 ng-messages 验证此表单,例如当日期为将来时会显示错误消息。


有什么问题

当我将自定义错误消息嵌入到组件中时,绑定失败。有一个错误(Angular-material 在输入字段下方显示一条红线)但属于该错误的消息未显示。


什么时候起作用?

当我不包含 ng-messages,而是将它们与组件的 html 内联时,会显示错误消息。这里唯一的问题是表单名和输入名是动态的(通过组件绑定定义)所以我需要使用this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error'],我真的不想使用(来自GitHub AngularJS issues)。


它看起来怎么样?

WorkHoursController 中,我检查日期的有效性。如果一个无效,我从那里设置子表单输入字段的$setValidity。这就是我使用动态表单和表单项名称的原因(还有超过 1 个 datepicker.html 嵌入到 workhours.html 中,因此我不能使用静态名称)。


我想要的(但不起作用)

将整个 ng-message div 转入组件,不带this.$eval()

workhours.html

<div layout="column" layout-fill ng-cloak>
    <md-content>
        <form name="bigForm" novalidate>
            <div class="formCard md-whiteframe-2dp">
                <h4>Workhours</h4>
                <date-picker title="Select workhours"
                                       model="$ctrl.time.workhour"
                                       input-name="workhour">
                    <div ng-messages="workhourForm.workhour.$error">
                        <div ng-message="inFuture">You cannot plan workhours in the future</div>
                    </div>
                </date-picker>
            </div>
        </form>
    </md-content>
</div>

datePicker.html

<ng-form name="{{$ctrl.inputName}}Form">
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()">
        <input ng-model="$ctrl.formattedModel"
               aria-label="Open dateTimePicker"
               name="{{$ctrl.inputName}}"
               readonly
               ng-transclude>
    </md-input-container>
</ng-form>

以这种方式执行此操作时,将不会显示错误消息。只有红线。所以嵌入有问题。


我可以想到 2 个解决方法。两者都有缺点..

选项 1 - 工作代码

在组件本身中定义 ng-messages

日期选择器.html

<ng-form name="{{$ctrl.inputName}}Form">
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()">
        <input ng-model="$ctrl.formattedModel"
               aria-label="Open dateTimePicker"
               name="{{$ctrl.inputName}}"
               readonly>
        <div ng-messages="this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']">
            <div ng-message="inFuture">You cannot plan workhours in the future</div>
        </div>
    </md-input-container>
</ng-form>

这样所有的消息都在Datepicker.html中定义。因此,我无法添加任何自定义消息,只能使用预定义消息。这不是我想要的,因为这个组件可以在多种情况下使用,每种情况都有自己的业务规则。


选项 2 - 工作代码(有 1 个错误)

将 ng-messages 转入组件

datepicker.html

<ng-form name="{{$ctrl.inputName}}Form">
    <md-input-container class="md-block" ng-click="$ctrl.showPicker()">
        <input ng-model="$ctrl.formattedModel"
               aria-label="Open dateTimePicker"
               name="{{$ctrl.inputName}}"
               readonly>
        <div ng-messages="this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']" ng-transclude>

        </div>      
    </md-input-container>
</ng-form>

workhours.html

<div layout="column" layout-fill ng-cloak>   
    <md-content>
        <form name="bigForm" novalidate>
            <div class="formCard md-whiteframe-2dp">
                <h4>workhours</h4>
                <date-picker title="Select workhours"
                                       model="$ctrl.time.workhours"
                                       input-name="workhours">
                    <div ng-message="inFuture">You cannot plan workhours in the future</div>               
                </date-picker>
            </div>
        </form>
    </md-content>
</div>

使用这个选项,ng-messages 是动态的,可以添加任何你想要的。但是没有添加类md-input-message-animation。什么导致大文本和错误消息没有动画。这个类可以手动添加,但是在transclusion中出现了问题。

(左为错,右为右)


我的问题

我无法弄清楚两件事。有人可以帮我解决这两个问题吗?

  1. 如何在不使我的组件成为 指令(在 HTML 和组件之间放置一个指令 是一种选择)?
  2. 如何删除 this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error'] 的使用,并仍然使代码与动态表单和表单域名称一起使用?

提前致谢。

【问题讨论】:

    标签: javascript html angularjs angularjs-ng-transclude ng-messages


    【解决方案1】:

    在阅读了一些其他帖子并尝试了很多东西后,我找到了问题的答案。

    问题 1 的解决方案

    这是材料设计中的一个问题。我可以在我的 date-picker 组件 中使用$postLink 函数来解决这个问题。然而这个问题不是这个组件的责任。因此,一个也符合面向对象准则的解决方案我需要制定一个指令,将缺失的类添加到每个被嵌入的元素中。

    指令(custom-ng-message

    class CustomNgMessageLinkController {
        constructor(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) {
            element.addClass('md-input-message-animation');
        }
    }
    
    function CustomNgMessageDirective(): ng.IDirective {
       return {
           restrict: 'A',
           scope: {},
           link: CustomNgMessageLinkController
       };
    }
    

    使用此指令,我可以执行以下操作:

    <div custom-ng-message ng-message="inFuture">You cannot plan workhours in the future</div>
    

    这将转化为预期的结果:


    问题 2 的解决方案

    我的解决方案基于我的问题中的“选项 2 - 工作代码(有 1 个错误)”示例。为了完成这项工作,我必须创建和额外的范围属性(formName),在控制器中创建表单名称(而不是像我以前那样在 HTML 中)并在返回字段的控制器中添加额外的函数(一个对象ng.INgModelController)。

    我已经在date-picker组件的构造函数中添加了

    constructor(private $scope: ng.IScope) {
        this.formName = this.inputName + 'Form';
    }
    

    以及日期选择器组件中的新方法

    /**
     * Gets the form of the scope and returns the input field
     * @return {ng.INgModelController}
     */
    getFormInput(): ng.INgModelController {
        let formName = this.inputName + 'Form';
        return this.$scope[formName][this.inputName];
    }
    

    此方法的作用与this.$eval 几乎相同。唯一的区别是它不会造成安全问题。

    现在我可以像这样在 HTML 中使用formName

    <ng-form name="{{$ctrl.formName}}">
        <md-input-container class="md-block" ng-click="$ctrl.showPicker()">
            <input ng-model="$ctrl.formattedModel"
                   aria-label="Open dateTimePicker"
                   name="{{$ctrl.inputName}}"
                   readonly>
            <div ng-messages="$ctrl.getFormInput().$error" ng-transclude>
            </div>
        </md-input-container>
    </ng-form>
    

    这允许我使用动态表单和输入名称。这很有用,因为我在一个页面上有多个此组件。


    我如何设置错误

    在我的 WorkHours 控制器中,我可以验证 MomentJS 日期并设置错误消息,如下所示:

    constructor($scope: IWorkHourScope) {
       $scope.$watch(() => {
           return this.time.workhour;
       }, (newValue: Moment, oldValue: Moment) => {
           if (newValue > moment()) {                       
              $scope.timeSheetForm.workHourForm.workhour.$setValidity('inFuture', false);
           }
       });
    }
    

    这也使得为我的表单项创建所需的接口变得更加容易,因为所有表单都有不同的名称,我可以为每个子表单分配正确的属性。

    interface IWorkDateForm {
        workhour: ng.INgModelController;
    }
    
    interface IIllNessDateForm {
        illness: ng.INgModelController;
    }
    
    interface ITimeForm extends ng.IFormController {
        workhourForm: IWorkDateForm;
        illNessForm: IIllNessDateForm;       
    }
       
    interface ITimeSheetScope extends ng.IScope {
        timeForm: ITimeForm;
    }
    

    这样我就不必在表单中使用“any”,并且可以从 AngularJS 库中自动完成。

    【讨论】:

      猜你喜欢
      • 2016-08-03
      • 1970-01-01
      • 2013-02-17
      • 2013-05-13
      • 2015-02-15
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 1970-01-01
      相关资源
      最近更新 更多