Search code examples
angularjsangularjs-ng-repeat

Angular (1.x) - Programmatically access NG-REPEAT from code


I'm trying to setup hotkeys for an old project that still uses angular 1.x and one of the features I was trying to add would be to select the first row from a table that is created with an NG-REPEAT. I've been able to add in other functionality such has moving the selected row up / down because I pass in the selected row on ng-click="setSelected(this)" which then lets me save the row and move it with selectedRow.$$prevSibiling or selectedRow.$$nextSibiling.

What I'm having a hard time figuring out is how can I set the selectedRow from the controller.

Here is a quick example:

http://plnkr.co/edit/6jPHlYwkgF5raRWt?open=lib%2Fscript.js

JS:

App.controller('ActivitiesCtrl', [function() {
  var vm = this;
  vm.selectedRow = "Not set";
  vm.activities = [
    {
      "id": 1,
      "code": "ABC",
      "person": "Joe"
    },
    {
      "id": 2,
      "code": "DFF",
      "person": "Sally"
    },
    {
      "id": 3,
      "code": "ABC",
      "person": "Sue"
    },
    {
      "id": 4,
      "code": "124",
      "person": "Sam"
    },
  ];
  vm.setSelected = function(row) {
    vm.selectedRow.selected = false;
    vm.selectedRow = row;
    vm.selectedRow.selected = true;
  }
  vm.moveNext = function() {
    vm.setSelected(vm.selectedRow.$$nextSibling)
  }
  vm.setFirst = function() {
    vm.setSelected("How do I set it...");
    // How to set it? vm.setSelected(?????)
  }
}]);

HTML:

<div ng-controller="ActivitiesCtrl as vm">
  <table>
    <thead>
      <th>Id</th>
      <th>Code</th>
      <th>Person</th>
    </thead>
    <tbody>
      <tr ng-repeat="activity in vm.activities track by activity.id" ng-click="vm.setSelected(this)" ng-class="{info: selected}">
        <td>{{activity.id}}</td>
        <td>{{activity.code}}</td>
        <td>{{activity.person}}</td>
      </tr>
    </tbody>
  </table>
{{vm.selectedRow | json}}
<hr />
<button ng-click="vm.setFirst()">Set First</button>
<button ng-click="vm.moveNext()">Next</button>
</div>

Solution

  • You can do this by setting the actual object from the array as selectedRow rather than using this and set the class by checking if selectedRow === activity in the ng-class.

    This approach doesn't require mutating the objects

    <tr
        ng-repeat="activity in vm.activities track by activity.id"
        ng-click="vm.setSelected(activity)"
        ng-class="{info: vm.selectedRow == activity}"
    >
    

    Then you can use Array#findIndex() to get the current selectedRow index in the array and if a next one exists use it or go back to the first.

    For the setFirst() you just use vm.activities[0]

    vm.selectedRow = null;
    
    vm.setSelected = function (row) {     
      vm.selectedRow = row;     
    };
    
    vm.moveNext = function () {
      const {selectedRow:curr, activities:act} = vm;
      if (curr !== null) {
        let idx = act.findIndex(e => e == curr) + 1;
        let next = act[idx] || act[0];        
        vm.setSelected(next);
      }      
    };
    
    vm.setFirst = function () {
      vm.setSelected(vm.activities[0]);
    };
    

    Working plunker