6

I use AngularJS with the ng-repeat directive to show an array of objects as a list.

<li ng-repeat="cue in cues" class="form-inline">
    <input type="text" ng-model="cues[$index].text" class="input-xlarge"/>
    {{cue.isNewest}}
</li>

The property "isNewest" is true on only one element of the array. I would like to set the keyboard focus on the text input of that item. How can I do that with AngularJS?

4 Answers 4

24

Here is another directive implementation that uses attrs.$observe:

myApp.directive('focus', function () {
  return function (scope, element, attrs) {
    attrs.$observe('focus', function (newValue) {
      newValue === 'true' && element[0].focus();
      // or, if you don't like side effects (see @Christophe's comment):
      //if(newValue === 'true')  element[0].focus();
    });
  }
});

Note that an interpolated DOM attribute value (i.e., {{cue.isNewest}}) always evaluates to a string, hence the reason newvalue is compared to the string 'true' rather than keyword true.

HTML:

<input type="text" ng-model="cues[$index].text" focus="{{cue.isNewest}}"
 class="input-xlarge" />{{cue.isNewest}}

This fiddle also has a method to toggle which item in the array should have the focus.

Note that if you do not load jQuery, we need to use element[0].focus() in the link function (not element.focus()) becaues jqLite doesn't have a focus() method.

Sign up to request clarification or add additional context in comments.

2 Comments

Accepted over asgoth's answer, because it worked the first time tried and is well expalined.
Expected an assignment or function call and instead saw an expression: I must say I have not seen a side-effect in an expression for years except in some one-liner contests, it should be 'if (cond) {focus here}'.
4

Since you would be manipulating the DOM, you will need to create a directive. Something like:

var app = angular.module('quirli', []);
app.directive('focusable', function() {
    return {
        restrict: 'A',
        scope: {
            focusable: '@'
        },
        link: function(scope, elm, attrs) {
            scope.$watch('focusable', function (value) {
                if (value) {
                    elm[0].focus();
                }
            });
        }
    };
});

Html:

<html ng-app="quirli" lang="en">
....  
<input type="text" ng-model="cues[$index].text" class="input-xlarge" focusable="{{cue.isNewest}}"/>

Note: untested.

4 Comments

Tanks asgoth for this piece of code. I have slightly modified it, added an app name, as shown here: docs.angularjs.org/guide/directive The focusing stuff now works, the problem now is that the text in the textboxes is omitted. I have to figure out what the cause is for that.
I'm now reading through the mentioned directive document. Twists my mind!
Use elm[0].focus() if jQuery is not loaded (well, even if it is loaded, it might be better to use this syntax, as it should always work).
Works OK ...unless you also use ng-focus / ng-blur. Then you'll run into "$digest already in progress" territory.
1

There is no special feature in AngularJS to receive focus. You could solve this with a $watch in your controller, but also with a directive.

2 Comments

I have looked into $watch, from here: docs.angularjs.org/api/ng.$rootScope.Scope However, I come to the conclusion that I can only watch changes of the model. But my controller would not be able to manipulate the DOM, nor is this good design, AFAIK.
The controller would be able to manipulate the dom, but it's not good design. So a directive it is for you :)
1

The other proposed answers work OK 9/10 times for me, but soon I was running in "$digest already in progress" fun.

I have a slightly modified version of the previous answers by asgoth and Mark Rajcok. Basically you inject the $timeout dependency and put the focus() call inside of a timeout(...). IIRC ng-focus does the same.

var app = angular.module('cgeers', []);
app.directive('focus', ["$timeout", function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.$watch(attrs.focus, function (value) {
                if (value) {
                    $timeout(function() { element[0].focus(); });
                }
            });
        }
    };
}]);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.