【问题标题】:Dynamically adding required attribute to select element动态添加所需属性到选择元素
【发布时间】:2013-09-09 22:16:35
【问题描述】:

我正在尝试连接一个适配器以使 ASP.Net MVC 为客户端验证发出的标记能够在 AngularJS 中工作,但我遇到了一个有趣的障碍。如果我通过指令编译函数动态添加required 属性:

var myApp = angular.module('myApp', []).directive('valRequired', function() {
    return {
        compile: function (element) {

            element.attr('required', 'required');

            return function() { /* other custom logic here */ }
        }
    };
});

select 元素不会按要求进行验证。只有在动态添加属性(jsFiddle)时才会出现问题。

澄清:我想按原样使用 MVC 的 @Html.TextBoxFor(...)。对于基于 DataAnnotations 的模型,它发出的 data-val-* 属性包含有关要运行哪些验证以及错误消息应该是什么的信息。我不是在寻找连接错误消息的帮助,我只需要能够连接一个指令,告诉inputselect 等使用required 验证。

【问题讨论】:

  • like ng-required="{expression to test}" ?
  • 并非如此。 ASP.Net MVC 使用@Html.TextBoxFor(...) 之类的东西发出data-val-required 属性。我想自动将它们转换为它们的 angularjs 等价物,并添加一些接线以将 MVC 发出的验证消息也插入到应用程序中。
  • 也许这会有所帮助:stackoverflow.com/questions/4844001/… 另外,ng-require 可以写成data-ng-require,仍然会被 angular 解析。
  • 如果你想获得 MVC 发射属性的等效角度(如 data-val-required),你可以创建自己的 @Html 扩展方法,如 @Html.AngularTextBoxFor(...) 并保存客户端性能惩罚。我已经在我的应用程序中做到了这一点。如果你有兴趣,我可以在某个时候进一步写出来。
  • @jandersen 我知道你来自哪里。但是,我宁愿不重新发明轮子。 MVC 已经非常出色地使用所有必要的信息来标记 DOM,以进行基于 DataAnnotations 的验证。我只是想用 angularJS 而不是 jQuery 验证来连接这些位。制作自定义 HtmlHelper 扩展方法仍然不能解决整个问题:我还需要连接验证消息,以便 @Html.ValidationMessageFor(...) 也能正常工作。

标签: asp.net-mvc angularjs data-annotations


【解决方案1】:

让我开始吧,这并不漂亮,但它确实有效。我尝试了不同的方法来使本机指令起作用,但无济于事。看起来这个指令执行的时候已经太晚了。

这将查找您的 data-val-required 属性并向元素添加验证。

它会触发所有相同的事情,所以myForm.mySelect.$valid 仍然可以和myForm.mySelect.$error.required 一样工作

http://jsfiddle.net/TheSharpieOne/knc8p/

var myApp = angular.module('myApp', []).directive('valRequired', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function (scope, elm, attr, ctrl) {
            if (!ctrl || !attr.valRequired) return;
            attr.required = true; // force truthy in case we are on non input element

            var validator = function (value) {
                if (attr.required && (value == '' || value === false)) {
                    ctrl.$setValidity('required', false);
                    return;
                } else {
                    ctrl.$setValidity('required', true);
                    return value;
                }
            };

            ctrl.$formatters.push(validator);
            ctrl.$parsers.unshift(validator);

            attr.$observe('required', function () {
                validator(ctrl.$viewValue);
            });
        }
    };
});


function MyCtrl($scope, $http) {
    $scope.model = {
        property: ''
    };
}

【讨论】:

  • 这似乎是使它工作的唯一方法,我同意。我在 GitHub 上的 AngularJS 代码库中进行了一些代码冲浪。似乎无法以可以在inputselect 指令之外使用的方式访问与ngModel 控制器挂钩的位。
【解决方案2】:

更新 我想了一个更好的方法来回答这个问题。旧答案低于新答案。


您可以获得对 AngularJS 的 required 指令的引用并将其应用于您自己的指令。这是一个可以执行此操作的代码示例:

var myApp = angular.module('myApp', []).directive('valRequired', function(requiredDirective) {
    var newDirective = {},
        angularDirective = requiredDirective[0]; //This assumes angular's required directive is the first one

    //Copy over all other properties of the angular directive
    angular.extend(newDirective, angularDirective);

    //Change the name of our directive back to valRequired
    newDirective.name = 'valRequired';

    //Provide our own logic in the linking function
    newDirective.link = function(scope, element, attr, ctrl){
        //Super call
        angularDirective.link.apply(this, arguments);
        if(attr.valRequired === 'required'){
            attr.$set('ngRequired', 'true');
        } else {
            attr.$set('ngRequired', 'false');
        }
    }

    return newDirective;
});

<input data-val-required="required" ng-model="foo" />

老答案

使用 jQuery 或 jQLite 的 attr() 方法不会改变 AngularJs' Attributes object。 Attributes 对象是指令用作其逻辑值的对象。

您还需要包含 ng-required 属性,尽管您不需要将任何角度表达式绑定到它。这个问题将帮助你: Html5 data-* with asp.net mvc TextboxFor html attributes

这样做的原因是我们需要强制 Angular 将指令应用于该节点。在模板的编译阶段之后更新属性不会通知 Angular 将新指令应用于节点。

这应该可行:

var myApp = angular.module('myApp', []).directive('valRequired', function() {
    return {
        priority : 101, //The priority needs to run higher than 100 to get around angularjs' default priority for ngRequired
        link: function (scope, element, attr) {

            if(attr.valRequired === 'true'){
                attr.$set('ngRequired', 'true');
            } else {
                attr.$set('ngRequired', 'false');
            }
        }
    };
});

<input ng-required data-val-required="true" ng-model="foo" />

【讨论】:

  • 如果不手动将 ng-required 添加到标签中,我无法使其正常工作,问题/问题的 cmets 提到这不是一个选项。
  • ng-required 只需要在那里,并且将根据 data-val-required 的值打开或关闭。这很容易通过 TextBoxFor 实现。更新答案以澄清这一点
  • ng-repeat 有什么关系?
  • 需要 ng-required 以便应用该指令。如果 ng-required 不存在,Angular 将不知道将 ngRequired 指令应用于此输入
  • 你的意思是ng-required,那么,不是ng-repeat?如果是这样,没关系=)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-24
  • 2011-05-12
  • 2016-09-12
  • 1970-01-01
  • 2017-10-04
  • 2014-02-17
相关资源
最近更新 更多