AngularJS - как получить ссылку на отфильтрованный результат ngRepeat

129

Я использую директиву ng-repeat с таким фильтром:

ng-repeat="item in items | orderBy:'order_prop' | filter:query | limitTo:4"

и я прекрасно вижу визуализированные результаты; Теперь я хочу запустить логику этого результата в моем контроллере. Вопрос в том, как я могу получить ссылку на элементы результатов?

Обновить:


Чтобы уточнить: я пытаюсь создать автозаполнение, у меня есть следующие данные:

<input id="queryInput" ng-model="query" type="text" size="30" placeholder="Enter query">

а затем отфильтрованные результаты:

<ul>
   <li  ng-repeat="item in items | orderBy:'order_prop' | filter:query | limitTo:4">{{item.name}}</li>
</ul>

теперь я хочу перейти к результату и выбрать один из элементов.

Шломи Шварц
источник

Ответы:

271

ОБНОВЛЕНИЕ : это более простой способ, чем то, что было раньше.

 <input ng-model="query">
 <div ng-repeat="item in (filteredItems = (items | orderBy:'order_prop' | filter:query | limitTo:4))">
   {{item}}
 </div>

Тогда $scope.filteredItemsдоступно.

Эндрю Джослин
источник
Спасибо за ответ, смотрите мое обновление. Как бы вы это реализовали, обратите внимание, что я получаю весь список элементов при инициализации
Шломи Шварц
супер круто, спасибо за это. Я бы хотел, чтобы это была встроенная функция NG
Шломи Шварц
3
Проблема с этим подходом в том, что если вы посмотрите назначенное свойство, вы закончите спамом $ дайджестов (по одному на итерацию) ... Может быть, есть способ избежать этого?
Юхо Вепсяляйнен
1
Если я смотрю объект поиска и console.log, filteredItemsя всегда получаю данные одним фильтром позже. Это не результат только что измененного фильтра, а результат предыдущего изменения. Любые идеи?
ProblemsOfSumit
4
Может кто-нибудь объяснить мне, почему это работает? Я могу понять, что переменная из $ scope доступна в повторяющейся области (наследование области), но наоборот? Как? Приветствуется некоторая документация. Спасибо
dalvarezmartinez1
30

Вот версия с фильтром решения Энди Джослина.

Обновление: ПРЕРЫВАНИЕ ИЗМЕНЕНИЙ. Начиная с версии 1.3.0-beta.19 ( эта фиксация ) фильтры не имеют контекста и thisбудут привязаны к глобальной области. Вы можете передать контекст в качестве аргумента или использовать новый синтаксис псевдонимов в ngRepeat , 1.3.0-beta.17 +.

// pre 1.3.0-beta.19
yourModule.filter("as", function($parse) {
  return function(value, path) {
    return $parse(path).assign(this, value);
  };
});

// 1.3.0-beta.19+
yourModule.filter("as", function($parse) {
  return function(value, context, path) {
    return $parse(path).assign(context, value);
  };
});

Тогда на ваш взгляд

<!-- pre 1.3.0-beta.19 -->
<input ng-model="query">
<div ng-repeat="item in items | orderBy:'order_prop' | filter:query | limitTo:4 | as:'filteredItems'">
 {{item}}
</div>

<!-- 1.3.0-beta.19+ -->
<input ng-model="query">
<div ng-repeat="item in items | orderBy:'order_prop' | filter:query | limitTo:4 | as:this:'filteredItems'">
 {{item}}
</div>

<!-- 1.3.0-beta.17+ ngRepeat aliasing -->
<input ng-model="query">
<div ng-repeat="item in items | orderBy:'order_prop' | filter:query | limitTo:4 as filteredItems">
 {{item}}
</div>

Что дает вам доступ к $scope.filteredItems.

kevinjamesus86
источник
Спасибо! Не могли бы вы объяснить, как работает код фильтра? Я не знаком с $ parse, и документы angular не помогли мне расшифровать, как именно работает ваш фильтр.
Magne
2
@Magne Нет проблем! Пожалуйста, ознакомьтесь с обновлением, так как оно может избавить вас от боли и страданий в будущем. Чтобы ответить на ваш вопрос, вкратце, $parseэто компилятор выражений Angular . Например, он примет строку 'some.object.property' и вернет функцию, которая позволяет вам либо установить, либо получить значение по указанному пути для указанного контекст. Я использую его для настройки результатов фильтрации здесь, в частности, в $ scope. Надеюсь, это ответит на ваш вопрос.
kevinjamesus86
23

Попробуйте что-то вроде этого, проблема с ng-repeat в том, что он создает дочернюю область, из-за которой вы не можете получить доступ

filteritems

от контроллера

<li ng-repeat="doc in $parent.filteritems = (docs | filter:searchitems)" ></li>
имал хасаранга перера
источник
16

Обновить:

Даже после 1.3.0. если вы хотите , чтобы поставить его на сферу действия контроллера или родитель , вы не можете сделать это с помощью, как синтаксис. Например, если у меня есть следующий код:

<div>{{labelResults}}</div>
<li ng-repeat="label in (labels | filter:query | as labelResults)">
</div>

вышеуказанное не сработает. Чтобы обойти это, используйте $ parent так:

<li ng-repeat="label in ($parent.labelResults = (labels | filter:query))">
Гал Браха
источник
Стоит отметить, что если у вас есть фильтр limitTo для этого. Это не будет отражено в $ parent.labelResults
Zack
Если я console.log labelResultsвсегда получаю данные одним фильтром позже. Это не результат только что измененного фильтра, а результат предыдущего изменения. У вас не было этой проблемы?
Анна
10

Я придумал несколько лучшую версию решения Энди. В своем решении ng-repeat устанавливает наблюдение за выражением, содержащим присвоение. Каждый цикл дайджеста будет оценивать это выражение и присваивать результат переменной области видимости.

Проблема с этим решением заключается в том, что вы можете столкнуться с проблемами назначения, если находитесь в дочерней области. По этой же причине у вас должна быть точка в ng-модели .

Еще одна вещь, которая мне не нравится в этом решении, заключается в том, что оно скрывает определение фильтрованного массива где-то в разметке представления. Если он используется в нескольких местах вашего представления или вашего контроллера, это может сбить с толку.

Более простое решение - просто поместить часы в контроллер на функцию, которая выполняет назначение:

$scope.$watch(function () {
    $scope.filteredItems = $scope.$eval("items | orderBy:'order_prop' | filter:query | limitTo:4");
});

Эта функция будет оцениваться во время каждого цикла дайджеста, поэтому производительность должна быть сопоставима с решением Энди. Вы также можете добавить любое количество назначений в функцию, чтобы все они находились в одном месте, а не разбросаны по представлению.

В представлении вы просто использовали бы отфильтрованный список напрямую:

<ul>
    <li  ng-repeat="item in filteredItems">{{item.name}}</li>
</ul>

Вот скрипка, где это показано в более сложном сценарии.

Дэйв Джонсон
источник
Супер чисто и не загрязняет конструкцию, приятно!
kuzyn 08
это решение идеально подходит для меня ... я использовал повтор коллекции ionic вместо ng repeat ... вот почему принятый ответ не сработал для меня
Lakshay