提问



在AngularJS中将注意力集中在输入字段上的角度方式是什么?


更具体的要求:



  1. 打开模态时,将焦点设置在此模态内的预定义<input>上。

  2. 每次<input>变得可见(例如,通过点击某个按钮),将焦点设置在它上面。



我尝试用autofocus来实现第一个要求,但这只适用于第一次打开Modal时,并且仅在某些浏览器中(例如在Firefox中它不起作用)。[86] [[[87]


任何帮助将不胜感激。

最佳参考



  

      
  1. 打开模态时,将焦点设置在此模态内的预定义< input>。

  2.   



定义一个指令并让它$ watch一个属性/触发器,以便它知道何时聚焦元素:


Name: <input type="text" focus-me="shouldBeOpen">





app.directive('focusMe', ['$timeout', '$parse', function ($timeout, $parse) {
    return {
        //scope: true,   // optionally create a child scope
        link: function (scope, element, attrs) {
            var model = $parse(attrs.focusMe);
            scope.$watch(model, function (value) {
                console.log('value=', value);
                if (value === true) {
                    $timeout(function () {
                        element[0].focus();
                    });
                }
            });
            // to address @blesh's comment, set attribute value to 'false'
            // on blur event:
            element.bind('blur', function () {
                console.log('blur');
                scope.$apply(model.assign(scope, false));
            });
        }
    };
}]);


Plunker [88]


似乎需要$ timeout来给出模态时间来渲染。



  2。每次< input>变得可见(例如,通过单击某个按钮),将焦点设置在它上面。



创建一个基本类似于上面的指令。观察一些范围属性,当它变为真时(在你的ng-click处理程序中设置它),执行element[0].focus()。根据您的使用情况,您可能需要或不需要$ timeout超时:


<button class="btn" ng-click="showForm=true; focusInput=true">show form and
 focus input</button>
<div ng-show="showForm">
  <input type="text" ng-model="myInput" focus-me="focusInput"> {{ myInput }}
  <button class="btn" ng-click="showForm=false">hide form</button>
</div>





app.directive('focusMe', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      scope.$watch(attrs.focusMe, function(value) {
        if(value === true) { 
          console.log('value=',value);
          //$timeout(function() {
            element[0].focus();
            scope[attrs.focusMe] = false;
          //});
        }
      });
    }
  };
});


Plunker [89]





2013年7月更新:我见过一些人使用我原来的隔离范围指令,然后遇到嵌入式输入字段的问题(即模态中的输入字段)。没有新范围的指令(或者可能是新的子范围)应该减轻一些痛苦。所以上面我更新了不使用隔离范围的答案。以下是原始答案:


原始答案为1.,使用隔离范围:


Name: <input type="text" focus-me="{{shouldBeOpen}}">





app.directive('focusMe', function($timeout) {
  return {
    scope: { trigger: '@focusMe' },
    link: function(scope, element) {
      scope.$watch('trigger', function(value) {
        if(value === "true") { 
          $timeout(function() {
            element[0].focus(); 
          });
        }
      });
    }
  };
});


Plunker。[90]


2.使用隔离范围的原始答案:


<button class="btn" ng-click="showForm=true; focusInput=true">show form and
 focus input</button>
<div ng-show="showForm">
  <input type="text" focus-me="focusInput">
  <button class="btn" ng-click="showForm=false">hide form</button>
</div>





app.directive('focusMe', function($timeout) {
  return {
    scope: { trigger: '=focusMe' },
    link: function(scope, element) {
      scope.$watch('trigger', function(value) {
        if(value === true) { 
          //console.log('trigger',value);
          //$timeout(function() {
            element[0].focus();
            scope.trigger = false;
          //});
        }
      });
    }
  };
});


Plunker。[91]


由于我们需要重置指令中的trigger/focusInput属性,因此=用于双向数据绑定。在第一个指令中,@就足够了。另请注意,使用@时,我们将触发值与true进行比较,因为@始终会生成一个字符串。

其它参考1


(编辑:我已在此解释下添加了更新的解决方案)



Mark Rajcok是男人......他的答案是一个有效的答案,但 有缺陷(对不起Mark)......


...尝试使用布尔值聚焦输入,然后模糊输入,然后尝试使用它再次聚焦输入。除非你将boolean重置为false,然后$ digest,然后将其重置为true,否则它将无法工作。即使你在表达式中使用字符串比较,你也将被迫将字符串更改为其他内容,$ digest ,然后将其更改回来。(这已经通过模糊事件处理程序解决了。)


所以我建议这个替代解决方案:


使用事件,Angular的遗忘功能。



毕竟JavaScript喜欢事件。事件本质上是松耦合的,甚至更好,你避免在$ digest中添加另一个$ watch。


app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on(attr.focusOn, function(e) {
          elem[0].focus();
      });
   };
});


所以现在你可以像这样使用它:


<input type="text" focus-on="newItemAdded" />


然后在你的应用程序的任何地方


$scope.addNewItem = function () {
    /* stuff here to add a new item... */

    $scope.$broadcast('newItemAdded');
};


这很棒,因为你可以用这样的东西做各种各样的事情。首先,您可以绑定已经存在的事件。另一方面,您可以通过让应用的不同部分发布应用的其他部分可以订阅的事件来开始做一些聪明的事情。


无论如何,这种类型的东西尖叫事件驱动给我。我认为作为Angular开发人员,我们非常努力地将范围形状的钉子敲入事件形状孔。


这是最好的解决方案吗?我不知道。它是 解决方案。





更新的解决方案



在@ShimonRachlenko的评论之后,我已经改变了我的做法。现在我使用服务和指令处理幕后事件的组合:


除此之外,它与上述原理相同。


这是一个快速演示Plunk [92]


用法



<input type="text" focus-on="focusMe"/>


app.controller('MyCtrl', function($scope, focus) {
    focus('focusMe');
});


来源



app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on('focusOn', function(e, name) {
        if(name === attr.focusOn) {
          elem[0].focus();
        }
      });
   };
});

app.factory('focus', function ($rootScope, $timeout) {
  return function(name) {
    $timeout(function (){
      $rootScope.$broadcast('focusOn', name);
    });
  }
});

其它参考2


当你真正需要的是这个时,我发现其他一些答案过于复杂


app.directive('autoFocus', function($timeout) {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            $timeout(function(){
                _element[0].focus();
            }, 0);
        }
    };
});


用法是


<input name="theInput" auto-focus>


我们使用超时来让dom渲染中的东西,即使它是零,它至少等待它 - 这种方式在模态中也适用

其它参考3


HTML有一个属性autofocus


<input type="text" name="fname" autofocus>


http://www.w3schools.com/tags/att_input_autofocus.asp[93]

其它参考4


您还可以使用内置于angular的jqlite功能。


angular.element('.selector').trigger('focus');

其它参考5


这很有效,也是一种聚焦输入控制的角度方式


angular.element('#elementId').focus()


这虽然不是完成任务的纯粹角度方式,但语法遵循角度样式。 Jquery使用Angular(jQLite => JQuery Light)间接地扮演角色并直接访问DOM。


如果需要,可以很容易地将此​​代码放在一个简单的角度指令中,其中元素可以直接访问。

其它参考6


我不认为$ timeout是将元素集中在创建上的好方法。这是一种使用内置角度功能的方法,从角度文档的阴暗深处挖掘出来。注意链接属性如何分割进入前和后,用于预链接和后链接功能。


工作示例:http://plnkr.co/edit/Fj59GB [94]


// this is the directive you add to any element you want to highlight after creation
Guest.directive('autoFocus', function() {
    return {
        link: {
            pre: function preLink(scope, element, attr) {
                console.debug('prelink called');
                // this fails since the element hasn't rendered
                //element[0].focus();
            },
            post: function postLink(scope, element, attr) {
                console.debug('postlink called');
                // this succeeds since the element has been rendered
                element[0].focus();
            }
        }
    }
});


<input value="hello" />
<!-- this input automatically gets focus on creation -->
<input value="world" auto-focus />


完整的AngularJS指令文件:https://docs.angularjs.org/api/ng/service/$compile [95]

其它参考7


我已经编写了一个双向绑定焦点指令,就像最近的模型一样。


你使用像这样的焦点指令:


<input focus="someFocusVariable">


如果在控制器的任何位置使someFocusVariable范围变量为true,则输入得到焦点。如果您模糊输入,someFocusVariable将设置为false。这就像马克拉杰科克的第一个答案,但有双向约束。


这是指令:


function Ctrl($scope) {
  $scope.model = "ahaha"
  $scope.someFocusVariable = true; // If you want to focus initially, set this to true. Else you don't need to define this at all.
}

angular.module('experiement', [])
  .directive('focus', function($timeout, $parse) {
    return {
      restrict: 'A',
      link: function(scope, element, attrs) {
          scope.$watch(attrs.focus, function(newValue, oldValue) {
              if (newValue) { element[0].focus(); }
          });
          element.bind("blur", function(e) {
              $timeout(function() {
                  scope.$apply(attrs.focus + "=false"); 
              }, 0);
          });
          element.bind("focus", function(e) {
              $timeout(function() {
                  scope.$apply(attrs.focus + "=true");
              }, 0);
          })
      }
    }
  });


用法:


<div ng-app="experiement">
  <div ng-controller="Ctrl">
    An Input: <input ng-model="model" focus="someFocusVariable">
    <hr>
        <div ng-click="someFocusVariable=true">Focus!</div>  
        <pre>someFocusVariable: {{ someFocusVariable }}</pre>
        <pre>content: {{ model }}</pre>
  </div>
</div>


这是小提琴:


http://fiddle.jshell.net/ubenzer/9FSL4/8/[96]

其它参考8


这是我原来的解决方案:


plunker [97]


var app = angular.module('plunker', []);
app.directive('autoFocus', function($timeout) {
    return {
        link: function (scope, element, attrs) {
            attrs.$observe("autoFocus", function(newValue){
                if (newValue === "true")
                    $timeout(function(){element[0].focus()});
            });
        }
    };
});


和HTML:


<button ng-click="isVisible = !isVisible">Toggle input</button>
<input ng-show="isVisible" auto-focus="{{ isVisible }}" value="auto-focus on" />


它的作用:



它通过ng-show变为可见的输入。这里不使用$ watch或$。

其它参考9


对于那些将Angular与Bootstrap插件一起使用的人:


http://angular-ui.github.io/bootstrap/#/modal[98]


你可以挂钩模态实例的opened承诺:


modalInstance.opened.then(function() {
        $timeout(function() {
            angular.element('#title_input').trigger('focus');
        });
    });

modalInstance.result.then(function ( etc...

其它参考10


而不是创建自己的指令,可以简单地使用javascript函数来完成焦点。


这是一个例子。


在html文件中:


<input type="text" id="myInputId" />


在文件javascript中,例如在控制器中,您要激活焦点:


document.getElementById("myInputId").focus();

其它参考11


不要复活僵尸或插入我自己的指令(好吧,这正是我正在做的):


https://github.com/hiebj/ng-focus-if[99]


http://plnkr.co/edit/MJS3zRk079Mu72o5A9l6?p=preview[100]


<input focus-if />

(function() {
    'use strict';
    angular
        .module('focus-if', [])
        .directive('focusIf', focusIf);

    function focusIf($timeout) {
        function link($scope, $element, $attrs) {
            var dom = $element[0];
            if ($attrs.focusIf) {
                $scope.$watch($attrs.focusIf, focus);
            } else {
                focus(true);
            }
            function focus(condition) {
                if (condition) {
                    $timeout(function() {
                        dom.focus();
                    }, $scope.$eval($attrs.focusDelay) || 0);
                }
            }
        }
        return {
            restrict: 'A',
            link: link
        };
    }
})();

其它参考12


首先,关注的官方方法是1.1的路线图。同时,您可以编写指令来实现设置焦点。[101]


其次,在项目变得可见之后将焦点设置在当前需要一种解决方法。只需使用$timeout延迟对元素焦点()的调用。


因为焦点,模糊和选择存在相同的controller-modifies-DOM问题,所以我建议使用ng-target指令:


<input type="text" x-ng-model="form.color" x-ng-target="form.colorTarget">
<button class="btn" x-ng-click="form.colorTarget.focus()">do focus</button>


角斗程在这里:http://goo.gl/ipsx4,更多详情请在此处博客:http://goo.gl/4rdZa [102] [103]


以下指令将在您的ng-target属性指定的控制器内创建.focus()函数。 (它也会创建.blur().select()。演示:http://jsfiddle.net/bseib/WUcQX/[104]

其它参考13


我发现使用通用表达式很有用。通过这种方式,您可以在输入文本有效时执行自动移动焦点等操作


<button type="button" moo-focus-expression="form.phone.$valid">


或者在用户完成固定长度字段时自动对焦


<button type="submit" moo-focus-expression="smsconfirm.length == 6">


当然,重点是加载后


<input type="text" moo-focus-expression="true">


指令的代码:


.directive('mooFocusExpression', function ($timeout) {
    return {
        restrict: 'A',
        link: {
            post: function postLink(scope, element, attrs) {
                scope.$watch(attrs.mooFocusExpression, function (value) {

                    if (attrs.mooFocusExpression) {
                        if (scope.$eval(attrs.mooFocusExpression)) {
                            $timeout(function () {
                                element[0].focus();
                            }, 100); //need some delay to work with ng-disabled
                        }
                    }
                });
            }
        }
    };
});

其它参考14


如果你只想要一个由ng-click控制的简单焦点。


HTML:


<input ut-focus="focusTigger">

<button ng-click="focusTrigger=!focusTrigger" ng-init="focusTrigger=false"></button>


指示:


'use strict'

angular.module('focus',['ng'])
.directive('utFocus',function($timeout){
    return {
        link:function(scope,elem,attr){
            var focusTarget = attr['utFocus'];
            scope.$watch(focusTarget,function(value){
                $timeout(function(){
                    elem[0].focus();
                });
            });
        }
    }
});

其它参考15


一个适用于模态的简单方法:


.directive('focusMeNow', ['$timeout', function ($timeout)
{
    return {
        restrict: 'A',

        link: function (scope, element, attrs)
        {


            $timeout(function ()
            {
                element[0].focus();
            });



        }
    };
}])





<input ng-model="your.value" focus-me-now />

其它参考16


你可以创建一个强制关注postLinking上的装饰元素的指令:


angular.module('directives')
.directive('autoFocus', function() {
    return {
        restrict: 'AC',
        link: function(_scope, _element) {
            _element[0].focus();
        }
    };
});


然后在你的HTML中:


<input type="text" name="first" auto-focus/> <!-- this will get the focus -->
<input type="text" name="second"/>


这适用于模态和ng-if切换元素,而不适用于ng-show,因为postLinking仅在HTML处理时发生。

其它参考17


马克和布莱什有很好的答案;然而,Mark有一个Blesh指出的缺陷(除了实现起来很复杂)之外,我觉得Blesh的回答在创建一个服务时会出现语义错误,该服务专门用于向前端发送焦点请求。他需要的是一种延迟事件的方法,直到所有指令都在倾听。


所以这就是我最终做的事情,它从Blesh的答案中窃取了很多,但保持了控制器事件的​​语义和后加载服务的分离。


这使得控制器事件可以轻松地用于除了仅关注特定元素之外的事物,并且还允许仅在需要时引起后加载功能的开销,这在许多情况下可能不是这样。


用法



<input type="text" focus-on="controllerEvent"/>


app.controller('MyCtrl', function($scope, afterLoad) {
  function notifyControllerEvent() {
      $scope.$broadcast('controllerEvent');
  }

  afterLoad(notifyControllerEvent);
});


来源



app.directive('focusOn', function() {
   return function(scope, elem, attr) {
      scope.$on(attr.focusOn, function(e, name) {
        elem[0].focus();
      });
   };
});

app.factory('afterLoad', function ($rootScope, $timeout) {
  return function(func) {
    $timeout(func);
  }
});

其它参考18


这也可以使用ngModelController。使用1.6+(不熟悉旧版本)。


HTML


<form name="myForm">
    <input type="text" name="myText" ng-model="myText">
</form>


JS


$scope.myForm.myText.$$element.focus();


-


N.B。:根据上下文,您可能需要包含超时功能。


N.B.²:使用controllerAs时,这几乎是一样的。只需用name="vm.myForm"和JS,vm.myForm.myText.$$element.focus();替换name="myForm"

其它参考19


可能是ES6时代最简单的解决方案。


添加以下一个衬垫指令使得HTML自动对焦属性对Angular.js有效。


.directive('autofocus', ($timeout) => ({link: (_, e) => $timeout(() => e[0].focus())}))


现在,您可以使用HTML5自动聚焦语法,如:


<input type="text" autofocus>

其它参考20


这里只是一个新手,但我能够使用这个指令在ui.bootstrap.modal中工作:[105]


directives.directive('focus', function($timeout) {
    return {
        link : function(scope, element) {
            scope.$watch('idToFocus', function(value) {
                if (value === element[0].id) {
                    $timeout(function() {
                        element[0].focus();
                    });
                }
            });
        }
    };
});


在$ modal.open方法中,我使用下面的内容来指出焦点应该放在哪个元素:


var d = $modal.open({
        controller : function($scope, $modalInstance) {
            ...
            $scope.idToFocus = "cancelaAteste";
    }
        ...
    });


在模板上我有这个:


<input id="myInputId" focus />

其它参考21


我编辑Mark RajcokfocusMe指令,以便在一个元素中进行多重聚焦。


HTML:


<input  focus-me="myInputFocus"  type="text">


在AngularJs控制器中:


$scope.myInputFocus= true;


AngulaJS指令:


app.directive('focusMe', function ($timeout, $parse) {
    return {
        link: function (scope, element, attrs) {
            var model = $parse(attrs.focusMe);
            scope.$watch(model, function (value) {
                if (value === true) {
                    $timeout(function () {
                        scope.$apply(model.assign(scope, false));
                        element[0].focus();
                    }, 30);
                }
            });
        }
    };
});

其它参考22


以下指令为我做了诀窍。使用相同的autofocus html属性进行输入。


.directive('autofocus', [function () {
    return {
        require : 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.focus();
        }
    };
}])

其它参考23


如果您正在使用modalInstance并拥有该对象,则可以在打开模态后使用then来执行操作。如果您没有使用modalInstance,并且硬编码以打开模态,则可以使用该事件。 $ timeout不是一个好的解决方案。


你可以做(​​Bootstrap3):


$("#" + modalId).on("shown.bs.modal", function() {
    angular.element("[name='name']").focus();
});


在modalInstance中,您可以查看库如何在打开模态后执行代码。


不要像这样使用$ timeout,$ timeout可以是0,1,10,30,50,200或更多,这将取决于客户端计算机,以及打开模态的过程。


不要使用$ timeout让方法告诉你什么时候可以集中注意力;)


我希望这有帮助! :)

其它参考24


如果在指令模板中注入了所需的焦点元素,则前面的所有答案都不起作用。
以下指令适用于简单元素或指令注入元素(我在 typescript 中编写)。它接受内部可聚焦元素的选择器。如果你只需要关注self元素 - 不要将任何选择器参数发送到指令:


module APP.Directives {

export class FocusOnLoadDirective implements ng.IDirective {
    priority = 0;
    restrict = 'A';

    constructor(private $interval:any, private $timeout:any) {

    }

    link = (scope:ng.IScope, element:JQuery, attrs:any) => {
        var _self = this;
        var intervalId:number = 0;


        var clearInterval = function () {
            if (intervalId != 0) {
                _self.$interval.cancel(intervalId);
                intervalId = 0;
            }
        };

        _self.$timeout(function(){

                intervalId = _self.$interval(function () {
                    let focusableElement = null;
                    if (attrs.focusOnLoad != '') {
                        focusableElement = element.find(attrs.focusOnLoad);
                    }
                    else {
                        focusableElement = element;
                    }
                    console.debug('focusOnLoad directive: trying to focus');
                    focusableElement.focus();
                    if (document.activeElement === focusableElement[0]) {
                        clearInterval();
                    }
                }, 100);

                scope.$on('$destroy', function () {
                    // Make sure that the interval is destroyed too
                    clearInterval();
                });

        });
    };

    public static factory = ():ng.IDirectiveFactory => {
        let directive = ($interval:any, $timeout:any) => new FocusOnLoadDirective($interval, $timeout);
        directive.$inject = ['$interval', '$timeout'];
        return directive;
    };
}

angular.module('common').directive('focusOnLoad', FocusOnLoadDirective.factory());


}


简单元素的用法示例:


<button tabindex="0" focus-on-load />


内部元素的用法示例(通常用于动态注入元素,如带模板的指令):


<my-directive focus-on-load="input" />


你可以使用任何jQuery选择器而不是输入

其它参考25


我想在寻找更好的解决方案而不是找到它之后为这个讨论做出贡献,而不得不创建它。


标准:
1.解决方案应独立于父控制器范围,以提高可重用性。
2.避免使用$ watch来监视某些条件,这既缓慢又增加了摘要循环的大小并使测试更加困难。
3.避免$ timeout或$ scope。$ apply()来触发摘要循环。
4.输入元素存在于使用指令的元素中。


这是我最喜欢的解决方案:


指示:


.directive('focusInput', [ function () {
    return {
        scope: {},
        restrict: 'A',
        compile: function(elem, attr) {
            elem.bind('click', function() {
                elem.find('input').focus();                
            });
        }        
    };
}]);


HTML:


 <div focus-input>
     <input/>
 </div>


我希望这能帮助那里的人!

其它参考26


这很容易..试试这个


HTML


<select id="ddl00">  
 <option>"test 01"</option>  
</select>


的javascript


document.getElementById("ddl00").focus();

其它参考27


我认为该指令是不必要的。使用HTML id和class属性选择所需的元素,并让服务使用document.getElementById或document.querySelector来应用焦点(或jQuery等价物)。


标记是标准的HTML/角度指令,添加了用于选择的id/类


<input id="myInput" type="text" ng-model="myInputModel" />


控制器广播事件


$scope.$emit('ui:focus', '#myInput');


在UI服务中使用querySelector - 如果有多个匹配(比如由于类),它将只返回第一个


$rootScope.$on('ui:focus', function($event, selector){
  var elem = document.querySelector(selector);
  if (elem) {
    elem.focus();
  }
});


您可能希望使用$ timeout()来强制摘要周期

其它参考28


只是扔一些咖啡。


app.directive 'ngAltFocus', ->
    restrict: 'A'
    scope: ngAltFocus: '='
    link: (scope, el, attrs) ->
        scope.$watch 'ngAltFocus', (nv) -> el[0].focus() if nv

其它参考29


不确定是否依赖超时是一个好主意,但这适用于ng-repeat,因为此代码运行后,angularjs更新DOM,因此您确保所有对象都在那里:


myApp.directive('onLastRepeat', [function () {
        return function (scope, element, attrs) {
            if (scope.$last) setTimeout(function () {
                scope.$emit('onRepeatLast', element, attrs);
            }, 1);
        };
    }]);
    //controller for grid
    myApp.controller('SimpleController', ['$scope', '$timeout', '$http', function ($scope, $timeout, $http)
    {
        var newItemRemoved = false;
        var requiredAlert = false;
        //this event fires up when angular updates the dom for the last item
        //it's observed, so here, we stop the progress bar
        $scope.$on('onRepeatLast', function (scope, element, attrs) {
            //$scope.complete();
            console.log('done done!');
            $("#txtFirstName").focus();
        });
    }]);