angularjs-directive - 输入字段如何设置焦点?

  显示原文与译文双语对照的内容

'Angular 方式'在输入字段中设置焦点的方法是什么?

更具体的需求:

  1. 打开模式时,将焦点设置在这里模式中的预定义 <input>
  2. 每次 <input> 变得可见( 例如。 单击某个按钮,将焦点放在它上面。

在某些浏览器中( 例如g,与 autofocus我试图实现第一个需求,但这只适用于当模态是第一次打开的时候,盘算。 在 Firefox 中,它不工作) 。

任何帮助都将被欣赏。

时间:

  1. 打开模式时,将焦点设置在这里模式内的预定义 <输入> 。

定义一个指令并让它 $watch 一个属性/触发器,这样它就知道什么时候应该关注元素:


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


app.directive('focusMe', 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

$timeout 似乎需要提供模式时间来渲染。

''2.。每次 <输入> 变为可见( 例如。 单击某个按钮,将焦点放在它上面。

创建一个基本类似于上面的指令。 观察某个范围属性,当它成为真正的( 在你的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


更新 7/2013: 我看到一些人使用了我最初的隔离作用域指令,然后在嵌入的输入字段中出现了问题( 例如,modal模式中的输入字段) 。 没有新作用域( 或者可能是一个新的子作用域)的指令应该减轻一些痛苦。 所以上我更新了不使用隔离作用域的答案。 下面是原始答案:

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

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

由于我们需要重置指令中的触发器/focusInput属性,'='用于双向数据绑定。 在第一个指令中,'@'足够了。 还请注意,在使用'@'时,我们将触发器值与"真"比较,因为 @ 总是导致字符串。

( 编辑:我在这个解释中添加了一个更新的解决方案)

Mark Rajcok就是那个人。。 他的回答是一个有效的答案,但它 有缺陷( 抱歉标记) 。。

。尝试使用布尔值聚焦输入,然后模糊输入,然后尝试使用它再次聚焦输入。 除非你将布尔值重设为 false,否则 $digest, 将不起作用,然后将它的重置为真。 即使你在表达式中使用了字符串比较,你也必须将字符串更改为其他内容,然后再将它的改为。 ( 这已经用模糊事件处理程序解决了。)

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

使用事件,Angular的遗忘特性。

JavaScript喜欢所有的事件。 事件天生就是松散耦合的,甚至更好,你可以避免将另一个 $watch 添加到你的$digest.


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 开发者我们真的很难把 $scope 形状的钉子变成事件形状孔。

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


更新的解决方案

在下面的@ShimonRachlenko's 评论之后,我改变了我做这个动作的方法。 现在我使用一个服务和一个处理事件"在幕后"的指令组合:

除此之外,它是上面列出的相同主体。

是一个快速演示 Plunk

用法


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

我发现当你真正需要的是这个时候,其他的一些答案会非常复杂


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

用法是


<input name="theInput" auto-focus>

我们使用超时来让事情在dom渲染,尽管它是零,它至少会等待- 那样它的工作方式在助动词和诸如此类的东西也

你还可以使用内置于 Angular jqlite功能。

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

我认为 $timeout 不是把元素集中在创建上的好方法。 这是一个使用内置 Angular 功能的方法,从 Angular 文档的模糊深度挖掘出来。 注意"链接"属性如何被分割为"预先"和"过账l",用于pre-link和post-link函数。

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


//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

这可以很好地工作,并且可以使用 Angular 来实现输入控制:


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

这是使用 Angular ( jQLite => JQuery光) 访问DOM的一种方法。 如果需要,这里代码可以轻松放置在一个简单的Angular 指令中,其中元素是直接可以访问的。

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

你使用的焦点指令如下:


<input focus="someFocusVariable">

如果在控制器中的任何位置都使someFocusVariable作用域变量成立,则输入获得焦点。 如果你"模糊"的输入,someFocusVariable被设置为 false 。 这就像Rajcok的第一个答案,但有双向绑定。

以下是该指令:


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/

请请注意,,我是新到 Angular 。 所以,这种方法有相关事情 等等 problems/bugs/performance 那样的,如果你认为有一个问题请告诉我,这样我就可以学习。

路线图上为 1.1, 首先,包是官方方式做焦点 同时,你可以编写一个指令来实现设置焦点。

其次,要在项目当前可见的情况下对它的进行集中设置,需要一个解决方法。 一个刚刚延迟为你转接元素

由于相同的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>

这里的Angular 线程:http://goo.gl/ipsx4,以及博客上的更多细节: http://goo.gl/4rdZa

下面的指令将在控制器内部创建一个 .focus() 函数,由 ng-target 属性指定。 ( 它也创建了一个 .blur() 和一个 .select() 。) 演示: http://jsfiddle.net/bseib/WUcQX/

如果你只是想要一个由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();
 });
 });
 }
 }
});

标记的标记和Blesh都严重的答案,但是,有一个缺陷,这个缺陷Blesh指出( 除了复杂的实现) Blesh还有一种语义错误在创建一个服务,该服务是专门的,我觉得,回答关于发送焦点请求到前端时,实际上他需要的只是一种方法来延迟事件,直到所有的指令在听。

这就是我最后做的事情,它从blesh的答案中偷取很多内容,但保持控制器事件和"加载后"服务的语义分离。

在许多cases,这使被感染的系统事件来很容易地迷上了好几个事情不单是围绕一特定元素,也允许以在需要"加载后"功能仅当它时,产生的开销,这样它才能不 be.

用法


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

...