Сортировка элементов в массиве AngularFire с использованием фильтра

Я использую Angular с Firebase и теперь застрял с ошибкой при попытке заставить фабрику работать с фильтром.

 app.factory('itemsFactory', ["$scope", '$rootScope', "$firebase", "simpleLogin", 
  function($scope, $rootScope, $firebase, simpleLogin) {
      var ref = new Firebase("https://------.firebaseio.com/");
      var items = $scope.items
      $scope.items = [];
      var sync = $firebase(ref);
      $scope.items = sync.$asArray();
      $rootScope.auth = simpleLogin;

      return items;
   }]);

   app.filter('orderObjectBy',['itemsFactory', function (itemsFactory) {
     return function (items) {
       var filtered = [];
       for (var i = 0; i < items.length; i++) {
         var item = items[i];
         if (item.hot) {
           filtered.push(item);
         }
       };
       for (var i = 0; i < items.length; i++) {
         var item = items[i];
         if (!item.hot) {
           filtered.push(item);
         }
       };
       return filtered;
     };
   }]);

Это связанный HTML:

< tr ng-repeat="item in items | orderObjectBy:'hot' track by $index">

Вот что я получил в консоли:

ngRepeat: элемент в элементах | orderObjectBy: "горячее" отслеживание по $index copyProvider%20%3C-%20%24scope%20%3C-%itemsFactory%20%3C-%orderObjectByFilter при ошибке (собственный)

Кажется, я напортачил с внедрением зависимостей. Но как правильно это сделать?


person walkthroughthecode    schedule 13.10.2014    source источник
comment
Если вы пытаетесь отображать только горячие элементы, почему бы не item in items | filter:{hot:true} или (что еще лучше) не извлекать только горячие элементы из Firebase (например, установив актуальность элемента в качестве его приоритета)?   -  person Frank van Puffelen    schedule 13.10.2014
comment
Идея состоит в том, чтобы сначала отсортировать горячие предметы, а затем все остальные, поэтому ваш вариант, к сожалению, мне не подойдет.   -  person walkthroughthecode    schedule 13.10.2014
comment
Итак: item in items | filter:{hot:true}, а затем item in items | filter:{hot:false}. Или: установите горячность в качестве приоритета для элементов и позвольте Firebase вернуть их в том порядке, в котором вы хотите.   -  person Frank van Puffelen    schedule 13.10.2014
comment
Спасибо за помощь! Ваш первый вариант - повторить мой ng-repeat дважды? Это на самом деле работает и дает мне правильный результат без фабрики, но не совсем СУХОЙ. Что касается опции Firebase — это круто, даже не знал о ней, но мне бы очень хотелось решить эту задачу Angule Way.   -  person walkthroughthecode    schedule 14.10.2014
comment
Похоже, ваш синтаксис немного не так. Думаю, следует читать: ng-repeat="item in items track by $id | orderObjectBy:'hot'". Обратите внимание, что отслеживание идет перед фильтром, и, кроме того, мы отслеживаем по $id (уникальный), а не по $index (подвижный).   -  person Kato    schedule 14.10.2014
comment
Тот факт, что вы пишете код для упорядочивания массива в методе с именем filter, должен быть явным признаком того, что сопоставление неверно. Вероятно, его можно заставить работать, но почему бы вам не использовать механизм упорядочения Firebase?   -  person Frank van Puffelen    schedule 14.10.2014
comment
@Kato: ваш комментарий может быть лучше в качестве ответа.   -  person Frank van Puffelen    schedule 14.10.2014
comment
Да, работал над этим. К сожалению, я не могу ответить чем-то короче главы. :)   -  person Kato    schedule 14.10.2014


Ответы (1)


Вы можете воспользоваться расширяемостью AngularFire, чтобы упорядочить список без директивы:

app.factory('HotList', function($firebase) {   
  function hotComparator(a,b) {
    if( a.hot === b.hot ) {
      // if both are hot or not, then sort by $id
      return strcmp(a.$id, b.$id);
    }
    else {
      // place hot items at the top
      return a.hot? -1 : 1;
    }
  }

  function strcmp(str1, str2) {
    //  http://phpjs.org/functions/strcmp/
    return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
  }

  return function(ref) {
    var list = $firebase(ref).$asArray();
    function resort() {
      list.sort(hotComparator);
    }
    list.$watch(resort);
    resort();
    return list;
  };
});

app.controller('ctrl', function(HotList) {
   // automagically sorted by hot/not and $id
   $scope.list = HotList(new Firebase(URL));
});

Чтобы ваша директива работала на месте, вам нужно скопировать массив и вызвать сортировку по копии, что будет намного проще.

app.filter('orderObjectBy', function($firebase) {   
  function hotComparator(a,b) {
    if( a.hot === b.hot ) {
      // if both are hot or not, then sort by $id
      return strcmp(a.$id, b.$id);
    }
    else {
      // place hot items at the top
      return a.hot? -1 : 1;
    }
  }

  function strcmp(str1, str2) {
    //  http://phpjs.org/functions/strcmp/
    return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1));
  }

  return function(items) {
    var list = items.slice();
    list.sort(hotComparator);
    return list;
  };
});

И ваше использование в представлении не совсем правильное, поэтому попробуйте так:

ng-repeat="item in items track by $id | orderObjectBy:'hot'"
person Kato    schedule 13.10.2014
comment
Спасибо за это понимание. Действительно элегантное решение. Но я получил undefined в $scope.list — должно быть, это какая-то глупая ошибка, которую я не могу отследить. - person walkthroughthecode; 15.10.2014