0

I'm just starting to get to grips with angular and I am trying to do something that I think should be pretty simple, but I can't find anyone who has posted with exactly the same scenario. I have a collection which is initiated with three objects and I am using ng-repeat to generate a set of input fields for each of these objects. When the SPA is initialised I want the first input field to have focus: I can do with with autofocus if necessary. When the user TABs off the last input field I add another object to the collection using ng-blur="addRecord($index)". When the DOM is refreshed I want the first field in the new object to have focus. The difference between my effort and all the examples I can find online is that all the examples initiate the addition using a button and an ng-click event.

Because the DOM element is going to be created on the fly, I think I need a custom directive with a $timeout but this seems like a lot of work for what should be a fairly standard requirement. I am using 1.3.x Can anyone show me the basics of how to write the directive or point me at a library that already exists that will do what I want. My current code is set out below.

HTML

<body ng-app="myApp">
        <div ng-controller="playerController">
            <ul>
                <li ng-repeat="player in players">
                    <input type="text" placeholder="FirstName" ng-model="player.firstName"></input>
                    <input type="text" placeholder="NicktName" ng-model="player.nickName"></input>
                    <input type="text" placeholder="SurnameName" ng-model="player.lastName" ng-blur="addNew($index)"></input>
                      {{player.firstName}} "{{player.nickName}}" {{player.lastName}}
                </li>
            </ul>

        </div>


    <script type="text/javascript" src="angular.js"></script>
    <script type="text/javascript" src="myApp.js"></script>
    </body>

myApp.js

var myApp = angular.module('myApp',[]);

myApp.controller('playerController',function($scope){
  $scope.players = [
    {
      "firstName":"Aaron",
      "lastName":"Reese",
      "nickName":"Star Wars",
      "givemefocus": "true"
    },
    {
      "firstName":"Ian",
      "lastName":"Soinne",
      "nickName":"Dominian",
      "givemefocus": "false"
    },
    {
      "firstName":"Aaron",
      "lastName":"Bailey",
      "nickName":"Fernando",
      "givemefocus": "false"
    }
  ];

  $scope.addNew = function($index){
    if($index == (players.length -1 )){
      $scope.newPlayer = { 
        "firstName":"",
        "lastName":"",
        "nickName":"",
        "givemefocus": "true"
      };
      $scope.players.push($scope.newPlayer);
    }
  } 
});
Aaron Reese
  • 131
  • 11

2 Answers2

2
app.directive('takefocus', function($timeout) {
  return function(scope, element, attrs) {
    scope.$watch(attrs.takefocus, function(value) {
      if (value) {
        $timeout(function() { element.focus(); });
      }
    });
  };
});

In html:

        <li ng-repeat="player in players">
            <input type="text" placeholder="FirstName" ng-model="player.firstName" takefocus="player.givemefocus"></input>
Petr Averyanov
  • 9,327
  • 3
  • 20
  • 38
  • Thanks Petr. Checking my understanding: This will add a $watch so that any element with takefocus will be evaluated and if the attribute value has changed then that element will get focus after the digest cycle has completed (because of the $timeout). In the event that there is more than one element that has changed then last one wins based on DOM rendering. If I was to change my code so that I iterate over the collection and either set givemefocus = false on all but the last one or remove the property completely, I would need to modify the directive to check if value == "true" – Aaron Reese Jan 19 '15 at 19:15
  • there is a check if (value) – Petr Averyanov Jan 19 '15 at 19:48
0

Add Id to first input <input id=input{{$index}}../> to find this input later in onBlur function.

<li ng-repeat="player in players">
  <input id="input{{$index}}" type="text" placeholder="FirstName" ng-model="player.firstName"></input>
  <input type="text" placeholder="NicktName" ng-model="player.nickName"></input>
  <input type="text" placeholder="SurnameName" ng-model="player.lastName" ng-blur="addNew($index)"></input>
  {{player.firstName}} "{{player.nickName}}" {{player.lastName}}
</li>  

Add $timeout to controller. In function addNew use $timeout with zero delay to wait to the end of DOM rendering. Then input can be found by getElementById in $timeout function.

myApp.controller('playerController',function($scope, $timeout)
{
  $scope.addNew = function($index){
    if($index == (players.length -1 )){
      $scope.newPlayer = { 
        "firstName":"",
        "lastName":"",
        "nickName":"",
        "givemefocus": "true"
      };

      $scope.players.push($scope.newPlayer);

      $timeout(function () 
      {
        document.getElementById("input" + ($index + 1)).focus();
      });
    }
  } 
});