javascriptangularjsangularjs-filtermvw

dynamic routing with array item in angularjs after filtering array


I have a problem with my angularjs app where my app is routing to the wrong page when using an ng-repeat array to determine the route.

data looks like this and is accessed in the person controller:

[
  {
    "name":"AJ lastname",
    "img_name":"AJ_lastname",
    "location":"Baltimore, Maryland",
    "info":"stuff"
  },
  {
    "name":"Albert lastname",
    "img_name":"Albert_lastname",
    "location":"Boston, Massachusetts",
    "info":"stuff"
  } // ... more data
]

html: (the anchor tag links to the person based on their index in the array (I believe this may be what I need to change to fix the problem, but I'm not sure)

<ul class="main-list">
  <li class="list-item fade" ng-repeat="student in students | filter:filter">
    <a href="/#person/{{$index}}">
    <img class="portrait listimg" ng-src="/images/{{student.img_name}}.jpg" alt="portrait of {{student.name}}">
    <h2>{{student.name}}</h2>
    <h4>{{student.location}}</h4>
    </a>
  </li>
</ul>

Routing from angular: (the route with '/person/:itemId' is routing to a page specific to a specific user, where their index in the array determines their id)

app.config(function ($routeProvider, $httpProvider) {
  $routeProvider
    .when('/list', {
      templateUrl: './js/templates/list.html',
      controller: 'ListController'
    })
    .when('/person/:itemId', {
      templateUrl: './js/templates/person.html',
      controller: 'PersonController'
    })
    .otherwise('/list');
});

Here is the controller for the dynamic page. It works perfectly for the original array, but once I attempt to sort the array, the index no longer corresponds to the correct student.

app.controller('PersonController', function ($scope, $http, $routeParams) {
  $scope.person = 'Someone\'s name';
  $http.get('../js/students.json').success(function (data) {
    $scope.allStudents = data;
    $scope.studentId = $routeParams.itemId;
    $scope.student = data[$scope.studentId];
  });

So the functional problem is that the index applies to the first student in the large array of data. It appears to work perfectly, and the correct data populates the page, but when I use the html/text input to filter the list, the original indices are updated on the html side, and they do not correspond to the original array. So the routing sends them to the wrong page.

How can I make the routing work even for a filtered list?


Solution

  • One way you can do this is by using a function which returns you the index a student had in the original array for each student in your ng-repeat.

    $scope.getIndex = function(student) {
        return $scope.students.indexOf(student);
    }
    

    You can then call the function in your list like:

    <a ng-href="/#person/{{getIndex(student)}}">
    

    This though is not quite the most performant code you could imagine.

    Another way would be to just temporarily store the index of the student as a property and use that one to reference it, again not quite the nicest solution:

    $scope.students = $scope.students.map(function(student, index) {
        student.index = index;
    
        return student;
    });
    

    And in the list:

    <a ng-href="/#person/{{student.index}}">
    

    However, if you can somehow assign the students a unique id that would definitely be the preferred way. That way you also make sure that you always reference the same student. If your students.json somehow changes between the time you create the list and the time the user clicks on an item you may reference the wrong one again...

    By the way always use ng-href when including placeholders in the link. Why you should do so is well described in the Angular API docs:

    Using Angular markup like {{hash}} in an href attribute will make the link go to the wrong URL if the user clicks it before Angular has a chance to replace the {{hash}} markup with its value. Until Angular replaces the markup the link will be broken and will most likely return a 404 error. The ngHref directive solves this problem.