以角度方式设置元素焦点


113

在查看了如何使用角度设置焦点元素的示例后,我看到它们中的大多数使用一些变量来监视然后设置焦点,并且它们中的大多数对于要设置焦点的每个字段使用一个不同的变量。在具有许多字段的形式中,这意味着存在许多不同的变量。

考虑到jquery的方式,但是想以角度的方式做到这一点,我提出了一个解决方案,我们使用元素的id将焦点设置在任何函数上,因此,由于我在角度上非常陌生,因此我希望得到一些意见那个方法是正确的,有任何问题,任何可以帮助我更好地做到这一点的方法。

基本上,我创建了一个指令,该指令监视用户使用指令定义的范围值或默认值的focusElement,并且当该值与元素的id相同时,该元素集将自动聚焦。

angular.module('appnamehere')
  .directive('myFocus', function () {
    return {
      restrict: 'A',
      link: function postLink(scope, element, attrs) {
        if (attrs.myFocus == "") {
          attrs.myFocus = "focusElement";
        }
        scope.$watch(attrs.myFocus, function(value) {
          if(value == attrs.id) {
            element[0].focus();
          }
        });
        element.on("blur", function() {
          scope[attrs.myFocus] = "";
          scope.$apply();
        })        
      }
    };
  });

由于某种原因需要引起关注的输入将采用这种方式

<input my-focus id="input1" type="text" />

这里有任何要设置焦点的元素:

<a href="" ng-click="clickButton()" >Set focus</a>

和设置焦点的示例函数:

$scope.clickButton = function() {
    $scope.focusElement = "input1";
}

这是一个好的角度解决方案吗?我的不良经验是否会带来一些我还没有看到的问题?

Answers:


173

您的解决方案的问题在于,当与其他创建新作用域的指令(例如)捆绑在一起时,它不能很好地工作ng-repeat。更好的解决方案是简单地创建一个服务函数,使您能够在控制器中强制性地集中元素或在html中声明性地集中元素。

演示

JAVASCRIPT

服务

 .factory('focus', function($timeout, $window) {
    return function(id) {
      // timeout makes sure that it is invoked after any other event has been triggered.
      // e.g. click events that need to run before the focus or
      // inputs elements that are in a disabled state but are enabled when those events
      // are triggered.
      $timeout(function() {
        var element = $window.document.getElementById(id);
        if(element)
          element.focus();
      });
    };
  });

指示

  .directive('eventFocus', function(focus) {
    return function(scope, elem, attr) {
      elem.on(attr.eventFocus, function() {
        focus(attr.eventFocusId);
      });

      // Removes bound events in the element itself
      // when the scope is destroyed
      scope.$on('$destroy', function() {
        elem.off(attr.eventFocus);
      });
    };
  });

控制者

.controller('Ctrl', function($scope, focus) {
    $scope.doSomething = function() {
      // do something awesome
      focus('email');
    };
  });

的HTML

<input type="email" id="email" class="form-control">
<button event-focus="click" event-focus-id="email">Declarative Focus</button>
<button ng-click="doSomething()">Imperative Focus</button>

我真的很喜欢这个解决方案。但是,您能解释一下使用$ timeout的原因吗?是由于“ Angular Thing”或“ DOM Thing”而使用它的原因?
user1821052 2014年

它确保它在与angular一样的任何摘要循环之后运行,但这不包括在超时后执行异步操作后受影响的摘要循环。
ryeballar 2014年

3
谢谢!对于那些想知道在
Angular

@ryeballar,谢谢!不错的简单解决方案。不过只是一个问题。我可以使用通过属性创建的工厂,而不是等待事件发生吗?
Pratik Gaikwad 2015年

4
仅仅为了集中输入而进行的角工作量真是太疯狂了。
布鲁诺·桑托斯

19

关于此解决方案,我们可以仅创建一个指令并将其附加到在满足给定条件时必须获得焦点的DOM元素。通过遵循这种方法,我们避免将控制器耦合到DOM元素ID。

示例代码指令:

gbndirectives.directive('focusOnCondition', ['$timeout',
    function ($timeout) {
        var checkDirectivePrerequisites = function (attrs) {
          if (!attrs.focusOnCondition && attrs.focusOnCondition != "") {
                throw "FocusOnCondition missing attribute to evaluate";
          }
        }

        return {            
            restrict: "A",
            link: function (scope, element, attrs, ctrls) {
                checkDirectivePrerequisites(attrs);

                scope.$watch(attrs.focusOnCondition, function (currentValue, lastValue) {
                    if(currentValue == true) {
                        $timeout(function () {                                                
                            element.focus();
                        });
                    }
                });
            }
        };
    }
]);

可能的用法

.controller('Ctrl', function($scope) {
   $scope.myCondition = false;
   // you can just add this to a radiobutton click value
   // or just watch for a value to change...
   $scope.doSomething = function(newMyConditionValue) {
       // do something awesome
       $scope.myCondition = newMyConditionValue;
  };

});

的HTML

<input focus-on-condition="myCondition">

1
myCondition$ scope变量已经设置为true,然后用户选择将焦点移到另一个元素上时,会发生什么情况?当该焦点myCondition为true时,您是否仍可以重新触发焦点,代码将监视该属性的更改,focusOnCondition但在以下情况下不会触发该更改:您尝试更改的值仍然相同。
ryeballar 2014年

我将更新示例,在我们的示例中,我们有两个单选按钮,并且根据值将标志切换为true或false,您可以将myCondition标志更改为true或false
Braulio

好像是通用解决方案。比根据ID更好。我喜欢。
mortb

万一其他人尝试了这种方法而没有用,我必须更改element.focus();。到element [0] .focus();
阿德里安·卡尔

1
这个解决方案比上面的基于id的黑客更“有角度”。
setec

11

我希望尽可能避免DOM查找,监视和全局发射器,因此我使用了更直接的方法。使用指令分配一个简单的函数,该函数着重于指令元素。然后在控制器范围内的任何地方调用该函数。

这是将其附加到示波器的简化方法。请参阅完整的代码片段以处理controller-as语法。

指示:

app.directive('inputFocusFunction', function () {
    'use strict';
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            scope[attr.inputFocusFunction] = function () {
                element[0].focus();
            };
        }
    };
});

并在html中:

<input input-focus-function="focusOnSaveInput" ng-model="saveName">
<button ng-click="focusOnSaveInput()">Focus</button>

或在控制器中:

$scope.focusOnSaveInput();

进行了编辑,以提供有关此方法原因的更多说明,并扩展代码段以供控制器使用。


很好,对我来说一直很好。但是现在我有了使用的一组输入ng-repeat,而我只想为第一个设置聚焦功能。知道如何例如<input>基于条件设置焦点功能$index吗?
Garret Wilson

很高兴这很有用。我的angular 1有点生锈,但是您应该能够向输入中添加另一个属性,例如assign-focus-function-if="{{$index===0}}",然后在指令不存在的情况下,在分配函数之前退出指令的第一行:if (attr.assignFocusFunctionIf===false) return; 注意,我正在检查是否它是显式的false,而不仅仅是虚假的,因此如果未定义该属性,该指令仍将起作用。
cstricklan

控制器-与lodash相比要简单得多。_.set(scope, attributes.focusOnSaveInput, function() { element.focus(); })
Atomosk

9

你可以试试

angular.element('#<elementId>').focus();

例如

angular.element('#txtUserId').focus();

它为我工作。


4
注意:这仅在使用完整的jQuery而不是依赖于Angular中嵌入的jqLit​​e时才有效。请参阅docs.angularjs.org/api/ng/function/angular.element
John Rix

4
这是执行此操作的jQuery方法,而不是有角度的方法。这个问题具体要求如何以一种有角度的方式做到这一点。
forgivenson

4

另一个选择是使用Angular的内置pub-sub体系结构,以通知您的指令集中精力。与其他方法类似,但是它并不直接绑定到属性,而是监听特定键的作用域。

指示:

angular.module("app").directive("focusOn", function($timeout) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$on(attrs.focusOn, function(e) {
        $timeout((function() {
          element[0].focus();
        }), 10);
      });
    }
  };
});

HTML:

<input type="text" name="text_input" ng-model="ctrl.model" focus-on="focusTextInput" />

控制器:

//Assume this is within your controller
//And you've hit the point where you want to focus the input:
$scope.$broadcast("focusTextInput");

3

我更喜欢使用表达式。这使我可以在字段有效,达到一定长度(当然是加载后)的情况下进行诸如集中于按钮的操作。

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

在复杂的形式上,这也减少了为集中目的而创建其他范围变量的需求。

参见https://stackoverflow.com/a/29963695/937997

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.