Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy dom manipulation in AngularJS - click a button, then set focus to an input element

I have this angular code:

<div class="element-wrapper" ng-repeat="element in elements">
  <div class="first-wrapper">
     <div class="button" ng-click="doSomething(element,$event)">{{element.name}}</div>   
  </div>
  <div class="second-wrapper">
    <input type="text" value="{{element.value}}">    
  </div>
</div>

What I want to happen: when the user clicks the button - the input element will be focused.

How do I find the input element after I click the button element and focus it?

I can do a function that looks like this:

function doSomething(element,$event) {
  //option A - start manipulating in the dark:
  $event.srcElement.parentNode.childNodes[1]

  //option B - wrapping it with jQuery:
   $($event.srcElement).closest('.element-wrapper').find('input').focus();
}

Neither of them work - Is there a nicer Angular way to do it? Using functions such as .closest() and .find() as in jQuery?

Update:

I found this hack to be working (but it still doesn't seem like the correct solution):

function doSomething(element,$event) {
   setTimeout(function(){
     $($event.srcElement).closest('.element-wrapper').find('input').focus();
   },0)
}

I am wrapping it with setTimeout so after Angular finishes all of its manipulations it focuses on the input element.

like image 947
Alon Avatar asked Mar 20 '13 08:03

Alon


People also ask

What is DOM manipulation in AngularJS?

When it comes to do DOM manipulation, binding event, etc... It happens, that we define functions that manipulates the DOM in a custom directive's link function, but we call it from the controller (we define functions in the $scope so it can be accessible by the given controller).


1 Answers

DOM manipulation should be in a directive instead of the controller. I would define a focusInput directive and use it on the button:

<div class="button" focus-input>{{element.name}}</div>   

Directive:

app.directive('focusInput', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      element.bind('click', function() {
        $timeout(function() {
          element.parent().parent().find('input')[0].focus();
        });
      });
    }
  };
});

Plunker

Since jqLite is rather limited in terms of DOM traversal methods, I had to use parent().parent(). You may wish to use jQuery or some JavaScript methods.

As you already found out, $timeout is needed so that the focus() method is called after the browser renders (i.e., finishes handling the click event).

find('input')[0] gives us access to the DOM element, allowing us to use the JavaScript focus() method (rather than find('input').focus() which would require jQuery).

like image 50
Mark Rajcok Avatar answered Oct 16 '22 02:10

Mark Rajcok