公开视图状态的AngularJS指令

时间:2014-01-30 08:17:25

标签: javascript angularjs angularjs-directive angularjs-scope

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

我的问题:

  1. 有没有办法可以轻松地从指令中公开isActive状态,以便调用者不必传入外部属性作为中介?这真的不是外部状态,它都是面向视图的,我只是想从HTML中干净地访问它。
  2. 如何在元素上附加和解析$ error,$ invalid等?我可以在用户代码中构建相同的功能吗?即element.state.active?

  3. <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

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

2 个答案:

答案 0 :(得分: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 =&gt;没有错误f.e.$error.active == true(有点颠倒你当前的逻辑)

DEMO

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

DEMO

答案 1 :(得分:1)

在@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;
                })


            }
        };
    }]);