문제

Similar to this question, I want to set focus on the last <select> whenever it gets added. As there's a single method doing it, I need no directive and no watch and no events. My function

$scope.addNew = function() {
    $scope.items.push({});
    $timeout(function() {
        $("select").focus();
    });
};

works nicely, except when called directly from the controller function definition like

angular.module('myModule').controller('MyCtrl', function($scope, $timeout) {
    $scope.items = {};
    ...
    $scope.addNew();
}

It looks like the timeout happens before the DOM gets constructed and $("select") is empty. With a delay of some 100 ms it works again, but this is a bad hack.

Contrary to what's said in the answer to the linked question, timeout doesn't suffice.

So what's a reliable way to wait for angularjs being really done with the DOM and everything?

Update:

It probably doesn't work because of the select to be focused being embedded in directives (including ng-repeat and some own ones) That's why there initially was no DOM element to focus on.

According to the comments, I need a directive. What's unclear is how exactly to do it. I tried and failed and found out a simpler solution.

What I need

I wasn't very explicit with this, so let me clarify.

  • I'm working with a table where each row contains some editable fields.
  • In addNew, I want to set focus on the first editable field of the new row.
  • In my case this happens to be the very last select.

It worked except at the very beginning, when I was adding the very first row from the controller body.

Why I'm opposed to using a directive

To my limited understanding, it's completely backwards:

  • A directive modifies the look, behavior, or structure of a given element. But there's no element which should be modified. I tried to put a directive on everything from the select itself to the whole body.
  • It needs to watch something or listen to an event, but I only want to invoke a function manually.
  • It didn't work (for me and others as the comments to the linked question shows).
도움이 되었습니까?

해결책

I am going to try and influence you to use a directive here, just to perform the behavior.
Here is a fiddle.

Basic premise is adding the behavioral directive to the element inside repeater:

<table>
     <tr ng-repeat="item in items">
         <td>{{item}}: <input type="text" auto-focus/></td>
     </tr>
</table>

Then your directive would put focus on the last added element:

app.directive('autoFocus', function(){ 
    return function link(scope, elem){
        elem[0].focus();   
    }
});

No watchers or events needed unless I am missing something that you require.

다른 팁

Code that manipulates the DOM should go in a directive, but if you switch to a directive and still have reason to wait until Angular is finished updating the scope and the dom, use $scope.$evalAsync:

$scope.$evalAsync( function() { 
  // This will wait until Angular is done updating the scope
  // Do some stuff here
  //
});

The solution was very trivial: Instead of calling $scope.addNew(); directly, I put it in $scope.init invoked from <form ng-init="init()">.

According to the documentation

The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.

this seems to be wrong (or maybe not, as ngRepeat si involved). I'm only using it to postpone the call to $scope.addNew();, where neither timeout nor posting events worked.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top