【问题标题】:AngularJS directive that exposes view state暴露视图状态的 AngularJS 指令
【发布时间】:2014-01-31 00:33:40
【问题描述】:

我有一个 AngularJS 指令,我用它来试验延迟表单字段验证,无论是在模糊还是在空闲时(一段时间没有输入)。我有两种方法连接起来;一种是在实际输入字段上添加一个类,另一种是在作用域上设置一个状态变量。设置类对于设置字段的样式是可以的,但我更关心的是帮助消息的样式(ng 显示),它位于一个不相关的元素上。另外这个类可以用ng-class添加,所以我想要的方法似乎应该是状态变量属性检查。

我的问题:

  1. 有没有一种方法可以轻松地从指令中公开 isActive 状态,这样调用者就不必传递外部属性来充当中介?这真的不是外部状态,它都是面向视图的,我只想从 HTML 中干净地访问它。
  2. $error、$invalid 等是如何附加到元素上并解决的?我可以在用户代码中构建相同的功能吗?即 element.state.active?

<form name="f">
        <div class="form-group" ng-class="{'has-error': f.e.$invalid}">
            <label>Enter Email Address</label>
            <input type="email" class="form-control" name="e" ng-model="user.email" required autofocus active="1500" is-active="state.active"/>
            <p class="help-block" ng-show="!state.active && f.e.$error.required">email is required</p>
            <p class="help-block" ng-show="!state.active && f.e.$error.email">enter a valid email address</p>
            <p class="help-block" ng-show="state.active">typing...</p>
            <p class="help-block" ng-show="!state.active && f.$valid">ok!</p>

        </div>
    </form>

以及相关的指令...

app.directive('active', ['$parse', function ($parse) {
        var tid;
        return {
            restrict: 'A',
            require: '?ngModel',
            scope:
            {
                isActive: "=",
                ngModel: "="
            },

            link: function (scope, elem, attrs, ctrl) {
                elem.bind('blur', function()
                {
                    scope.isActive = false;
                    elem.removeClass("ng-active");
                });
                var timeout = attrs.active || 2000;
                ctrl.$parsers.unshift(function(value)
                {
                    scope.isActive = true;
                    console.log("adding class");
                    elem.addClass("ng-active");
                    if (tid) clearTimeout(tid);

                    tid = setTimeout(function()
                    {
                        console.log("timeout elapsed, removing class");
                        scope.$apply(function(){
                        scope.isActive = false;
                        });
                        elem.removeClass("ng-active")


                    }, timeout);
                    return value;
                })


            }
        };
    }]);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function TestCtrl($scope)
{
    $scope.testName = "Validation Test";
    $scope.user = {
        email: 'test@foo.com'
    };
    $scope.state = {
        active: false
    };

}

这里是a fiddle of this code running

基本上,它可以工作,但我不喜欢我必须为每个输入字段传递一个外部属性,我不喜欢验证检查的不对称性。有没有更好的方法来做到这一点?

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    你可以试试:ngModelController.$setValidity

    app.directive('active', ['$parse', function ($parse) {
            var tid;
            return {
                restrict: 'A',
                require: '?ngModel',
                scope:
                {
                    ngModel: "="
                },
    
                link: function (scope, elem, attrs, ctrl) {
                    elem.bind('blur', function()
                    {
                        scope.$apply(function(){
                            ctrl.$setValidity("active",true);
                        });
                    });
                    var timeout = attrs.active || 2000;
                    ctrl.$parsers.unshift(function(value)
                    {
                        ctrl.$setValidity("active",false);
                        console.log("adding class");
    
                        if (tid) clearTimeout(tid);
    
                        tid = setTimeout(function()
                        {
                            ctrl.$setValidity("active",false);
                        }, timeout);
                        return value;
                    })
    
    
                }
            };
        }]);
    

    1) 像这样设置活动:ctrl.$setValidity("active",true);

    2) 像这样从 html 访问:f.e.$error.active

    3) 请注意,当我们使用此功能时:ctrl.$setValidity("active",true); 表示 active 没有错误 => f.e.$error.active == true(有点颠倒您当前的逻辑)

    DEMO

    另一种解决方案是将属性直接添加到您的 ngModelController:ctrl.active = false; 并在 html 中访问它,如下所示:f.e.active。但是这个解决方案不是很好的 IMO,因为当 angular 决定创建一个具有相同名称的新属性时,它可能会与 angular 的未来版本发生冲突。

    DEMO

    【讨论】:

    • 我想到了您的第一个解决方案,但由于“活动”实际上只是样式的信息位,而不是错误,因此将任何使表单无效的内容都不起作用。你的第二个答案就是我要找的;我可以发誓我试过了,不知道我搞砸了什么。我理解名称冲突的可能性,但应该有一种方法让用户以与 Angular 内部相同的方式扩展元素,这似乎是合理的。我会在上面加上一个唯一的前缀。谢谢!
    • @Jolly Roger:不需要发誓。祝你今天过得愉快。我很高兴它有帮助。
    【解决方案2】:

    为了以@Khanh TO 的(第二个)答案为基础,我更新了小提琴以更接近我的原作并删除了一些错别字。然后,我使用暴露的状态来设置帮助文本和输入字段的样式,并使用 ng-class。

    Go forth and fiddle!

    为了后代,HTML的相关位:

    <form name="f">
        <div class="form-group" ng-class="{'has-error': !f.e.active && f.e.$invalid}">
            <label>Enter Email Address</label>
            <input type="email" ng-class="{'active' : f.e.active}" class="form-control" name="e" ng-model="user.email" required autofocus active="1500" />
            <p class="help-block" ng-show="!f.e.active && f.e.$error.required">email is required</p>
            <p class="help-block" ng-show="!f.e.active && f.e.$error.email">enter a valid email address</p>
            <p class="help-block" ng-show="f.e.active">typing...</p>
            <p class="help-block" ng-show="!f.e.active && f.$valid">ok!</p>
    
        </div>
    </form>
    

    和 JS:

    app.directive('active', ['$parse', function ($parse) {
            var tid;
            return {
                restrict: 'A',
                require: '?ngModel',
    
                // warning - this adds an "active" property to ctrl; consider prefixing to avoid future collisions
                link: function (scope, elem, attrs, ctrl) {
                    elem.bind('blur', function()
                    {
                        ctrl.active = false;
                    });
                    var timeout = attrs.active || 2000;
                    ctrl.$parsers.unshift(function(value)
                    {
                        ctrl.active = true;
                        if (tid) clearTimeout(tid);
    
                        tid = setTimeout(function()
                        {
                            scope.$apply(function(){
                                ctrl.active = false;
                            });
    
                        }, timeout);
                        return value;
                    })
    
    
                }
            };
        }]);
    

    【讨论】:

      猜你喜欢
      • 2011-03-26
      • 2014-11-02
      • 1970-01-01
      • 1970-01-01
      • 2011-04-02
      • 1970-01-01
      • 2018-07-20
      • 1970-01-01
      • 2016-08-22
      相关资源
      最近更新 更多