Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Provide a standard way to manage focus #2012

Closed

Description

@ewinslow

Listening for focus/blur events is nice, but it frequently comes up that I need to set the focus on a certain element. E.g. clicking an "add comment" button opens up text field and sets focus inside the textarea. Clicking submit/cancel closes the form and puts focus back on the "add comment" button.

Related: #903 and #1277.

My initial thought was ng-focus-model="some.model". Setting some.model to true in the controller would then move focus to the element. But after experimenting with this, I've found that to be pretty unintuitive. This is one of the few cases where reaching into the DOM and just calling element.focus() seems like the right thing to do. Perhaps angular could provide a way to access dom elements from controllers (heresy!!) but with a severly limited API (i.e. only supports focus())

Activity

  1. pkozlowski-opensource commented on Jul 18, 2013

    @pkozlowski-opensource
    Member

    Some discussion regarding this in #1250 as well

  2. elgerlambert commented on Aug 6, 2013

    @elgerlambert

    There has been quite some discussion around this topic, so a proper solution requires more effort no doubt, but I thought I'd share the following which has served me well so far.

    angular.module('cn')
      .directive('cnFocusWhen', [function () {
        return function postLink(scope, element, attrs) {
          scope.$watch(attrs.cnFocusWhen, function( autofocus ) {
            if ( autofocus )
              setTimeout( function () {
                element[0].focus()
              }, 0)
          })
        }
      }])

    Sounds like my implementation is similar to that of @ewinslow's, the only real difference being syntax; "focus-model" to me implies it should only watch model properties, whereas I usually have a $scope.state object separate to my $scope.form model.

  3. elgerlambert commented on Aug 6, 2013

    @elgerlambert

    I opted for "focus-when", but I suppose ng-autofocus might fit better with Angular's motto "HTML enhanced"; adding the attribute autofocus to the element when ng-autofocus evaluates to true, similar to how ng-required works..

    // **Not tested!**
    angular.module('cn')
      .directive('cnAutofocus', [function () {
        return function postLink(scope, element, attrs) {
          scope.$watch(attrs.cnAutofocus, function( autofocus ) {
            if ( autofocus ) element.attr('autofocus','true')
            else element.removeAttr('autofocus')
          })
        }
      }])
  4. benlesh commented on Aug 18, 2013

    @benlesh
    Contributor

    Since this is a large change set, I've started a discussion on the mailing list here about it: https://groups.google.com/forum/?hl=en#!topic/angular/EQSWV1chf0A

    The spirit of the addition, at this point, is that I'm implementing a new controller, similar to an NgModelController, but seperate, as it only worries about managing focus. More details can be found at the link.

    I'll have a pull request ready soon.

  5. benlesh commented on Aug 19, 2013

    @benlesh
    Contributor

    I have the code ready, but I'm unable to build and run tests.

    I want to get this done and submit a pull request for your review, but currently I can't until I get this resolved #3648

  6. kevin-smets commented on Mar 6, 2014

    @kevin-smets

    $timeout or setTimeout works great indeed, but not so on mobile safari. You have to set the focus inside the event loop of ngClick in order for focus to work properly on iOS (triggering the keyboard), so any timeouts are off limits. I've looked around but found no mention of this problem anywhere, I can't imagine no one else encountered this yet though...

    The only way to go around this is by using event listeners, and $apply if it needs to update the model. Of course I would like to use the standard ngClick, it just doesn't seem possible at the moment without it throwing an error.

  7. bittenbytailfly commented on Mar 11, 2014

    @bittenbytailfly

    I'm fairly new to angular, but coming from KnockoutJS, I tried to replicate the 'hasfocus' data binding (see http://knockoutjs.com/documentation/hasfocus-binding.html) which seems to achieve what you want. Here's my effort:

    .directive('dbFocus', [function () {
            return {
                restrict: 'A',
                scope: {
                    dbFocus: '='
                },
                link: function (scope, elem, attrs) {
                    scope.$watch('dbFocus', function (newval, oldval) {
                        if (newval) {
                            elem[0].focus();
                        }
                    }, true);
                    elem.bind('blur', function () {
                        scope.$apply(function () { scope.dbFocus = false; });
                    });
                    elem.bind('focus', function () {
                        if (!scope.dbFocus) {
                            scope.$apply(function () { scope.dbFocus = true; });
                        }
                    });
                }
            };
        }]);
    

    It's probably not the most efficient way to go, but like I said I'm new so go easy on me! I prefixed it 'db' simply because that matches the app I was working on at the time.

  8. RoyceLithgo commented on Apr 8, 2014

    @RoyceLithgo

    Any progress on this? I've been racking my brain trying to get my directive to support the html5 "autofocus" boolean attribute, but angular seems determined not to allow me to do it.

  9. bittenbytailfly commented on Apr 8, 2014

    @bittenbytailfly

    RoyceLithgo - as I understand it, the autofocus attribute in html5 is only relevant on page load and does not effect behaviour after that.

    Have you tried the directive I listed above? I used it successfully in a Chrome extension but have not cross browser tested it. I see no reason why it would fail though, unless I've misunderstood what you are trying to achieve.

    Let me know if it's of any use to you.

  10. RoyceLithgo commented on Apr 9, 2014

    @RoyceLithgo

    Actually in the end I did something similar to get the autofocus supported in my directive. I added autofocus to my isolate scope and did this in the linking function:

    if(scope.autofocus === "true")
    {
    elm[0].childNodes[1].focus();
    }

    In my case the element was nested in the directive template, hence the path above. It works, but it does need you to set a value on the autofocus attribute whereas the html5 autofocus attribute doesn't need one. My logic was a lot simpler than yours, mainly because it doesn't support 2-way binding (in my use case, I will always set autofocus to a static "true" value on the first element in the form).

  11. btford commented on May 21, 2014

    @btford
    Contributor

    I think because the use cases for such a directive might differ, this is a good opportunity for 3rd party modules to implement different solutions.

    http://github.com/jgallen23/angular-focus looks like a good start.

    I'm going to close this as "not in core," but feel free to post solutions or links to modules here for others to find. Thanks!

  12. benlesh commented on May 21, 2014

    @benlesh
    Contributor

    I'm not sure I disagree, or agree. Given that there is a method for managing element validity built into Angular, why would a method for managing element focus in a similar fashion not also be a core feature? It might be nice for use in more primitive directives, like a custom calendar control for example, or maybe a custom color picker. Something were more fine-grained focus control might prove useful.

    Honest question. Thanks for your time, @btford.

  13. btford commented on May 21, 2014

    @btford
    Contributor

    honestly I think the form validation stuff should also be a separate module

  14. benlesh commented on May 21, 2014

    @benlesh
    Contributor

    Fair enough. I think I can agree with that.

  15. benlesh commented on May 21, 2014

    @benlesh
    Contributor

    Hahaha... my last comment reads funny. I'm sure you were just waiting around to see if I'd agree with you. ;) lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions