Ng-messages 动态表单和输入名称
Ng-messages dynamic form and input names
tl;博士;
好像不能嵌入 like ng-messages 指令。当您嵌入它时,它不会正确绑定。我有 2 个解决方法,但都有其缺点。谁有更好的解决方案还是我做错了什么?
我正在使用打字稿。
版本
AngularJS: 1.6.1
angular-动画:1.6.1
Angular-咏叹调:1.6.1
Angular-消息:1.6.1
Angular-material: 1.1.0
打字稿:3.10.5
我想要什么
我有一个格式化日期的组件(做更多,但不相关)并显示它。其中一些组件包含在同一页面上。围绕这些组件的是一个表单。我想使用自定义 ng-messages 验证此表单,假设日期是未来时显示错误消息。
什么问题
当我将自定义错误消息嵌入组件时,绑定失败。出现错误(Angular-material 在输入字段下方显示一条红线)但未显示属于该错误的消息。
什么时候生效?
当我不嵌入 ng 消息,而是将它们与组件的 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
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']">
<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 是动态的,可以添加任何你想要的。但是 class md-input-message-animation
没有添加。什么导致大文本和错误消息没有动画。这个class可以手动添加,但是在嵌入的时候有点问题。
(左边是错的,右边是对的)
我的问题
有两件事我想不通。有人可以帮我解决这两个问题吗?
- 如何在不使我的组件成为 a 的情况下嵌入 ng-messages
指令(在 HTML 和组件之间放置一个指令
是一个选项)?
- 如何删除
this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']
的用法并仍然使代码使用动态表单和表单域名称?
提前致谢。
在阅读了一些其他帖子并尝试了很多东西后,我找到了问题的答案。
问题1的解答
这是material设计的问题。我可能可以在我的 date-picker 组件 中使用 $postLink
函数来解决这个问题。然而这个问题不是这个组件的责任。因此,一个也符合面向对象指南的解决方案我需要制定一个指令,将缺失的 class 添加到每个被嵌入的元素中。
指令(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';
}
以及date-picker组件中的新方法:
/**
* 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 库中自动完成。
tl;博士;
好像不能嵌入 like ng-messages 指令。当您嵌入它时,它不会正确绑定。我有 2 个解决方法,但都有其缺点。谁有更好的解决方案还是我做错了什么?
我正在使用打字稿。
版本
AngularJS: 1.6.1
angular-动画:1.6.1
Angular-咏叹调:1.6.1
Angular-消息:1.6.1
Angular-material: 1.1.0
打字稿:3.10.5
我想要什么
我有一个格式化日期的组件(做更多,但不相关)并显示它。其中一些组件包含在同一页面上。围绕这些组件的是一个表单。我想使用自定义 ng-messages 验证此表单,假设日期是未来时显示错误消息。
什么问题
当我将自定义错误消息嵌入组件时,绑定失败。出现错误(Angular-material 在输入字段下方显示一条红线)但未显示属于该错误的消息。
什么时候生效?
当我不嵌入 ng 消息,而是将它们与组件的 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
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']">
<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 是动态的,可以添加任何你想要的。但是 class md-input-message-animation
没有添加。什么导致大文本和错误消息没有动画。这个class可以手动添加,但是在嵌入的时候有点问题。
(左边是错的,右边是对的)
我的问题
有两件事我想不通。有人可以帮我解决这两个问题吗?
- 如何在不使我的组件成为 a 的情况下嵌入 ng-messages 指令(在 HTML 和组件之间放置一个指令 是一个选项)?
- 如何删除
this.$eval($ctrl.inputName+'Form.'+$ctrl.inputName)['$error']
的用法并仍然使代码使用动态表单和表单域名称?
提前致谢。
在阅读了一些其他帖子并尝试了很多东西后,我找到了问题的答案。
问题1的解答
这是material设计的问题。我可能可以在我的 date-picker 组件 中使用 $postLink
函数来解决这个问题。然而这个问题不是这个组件的责任。因此,一个也符合面向对象指南的解决方案我需要制定一个指令,将缺失的 class 添加到每个被嵌入的元素中。
指令(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';
}
以及date-picker组件中的新方法:
/**
* 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 库中自动完成。