AngularJS: cómo obtener una referencia de resultado filtrada ngRepeat

129

Estoy usando una directiva ng-repeat con filtro así:

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

y puedo ver los resultados renderizados bien; ahora quiero ejecutar algo de lógica en ese resultado en mi controlador. La pregunta es ¿cómo puedo obtener la referencia de los elementos de resultado?

Actualizar:


Solo para aclarar: estoy tratando de crear un autocompletado, tengo esta entrada:

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

y luego los resultados filtrados:

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

ahora quiero navegar por el resultado y seleccionar uno de los elementos.

Shlomi Schwartz
fuente

Respuestas:

270

ACTUALIZACIÓN : Aquí hay una manera más fácil de lo que había antes

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

Entonces $scope.filteredItemses accesible.

Andrew Joslin
fuente
Gracias por la respuesta, vea mi actualización. ¿Cómo implementaría eso? Tenga en cuenta que obtengo la lista completa de elementos en init
Shlomi Schwartz
super genial, gracias por eso. Desearía que fuera una característica incorporada de NG
Shlomi Schwartz
3
El problema con este enfoque es que si observa la propiedad asignada, terminará enviando spam a $ digests (uno por iteración) ... ¿Tal vez hay alguna forma de evitar esto?
Juho Vepsäläinen
1
Si miro el objeto de búsqueda y console.log filteredItemssiempre obtengo los datos un filtro más tarde. No es el resultado del filtro recién cambiado, sino el del cambio anterior. ¿Algunas ideas?
ProblemsOfSumit
44
¿Alguien puede explicarme, por qué funciona esto? Puedo entender que una variable del $ alcance, es accesible en el alcance de repetición (herencia del alcance), pero al revés? ¿Cómo? Alguna documentación es apreciada. Gracias
dalvarezmartinez1
30

Aquí está la versión de filtro de la solución de Andy Joslin.

Actualización: ROMPER EL CAMBIO. A partir de la versión 1.3.0-beta.19 ( este commit ) los filtros no tienen contexto y thisestarán vinculados al alcance global. Puede pasar el contexto como argumento o usar la nueva sintaxis de alias en 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);
  };
});

Entonces en tu opinión

<!-- 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>

Lo que te da acceso a $scope.filteredItems.

kevinjamesus86
fuente
¡Gracias! ¿Podría explicar cómo funciona el código de filtro? No estoy familiarizado con $ parse, y los documentos angulares no me ayudaron a decodificar cómo funciona exactamente su filtro.
Magne
2
@ Magne No hay problema! Sin embargo, consulte la actualización, ya que puede ahorrarle un poco de dolor y sufrimiento en el futuro. Para responder a su pregunta, en resumen, $parsees el compilador de expresiones de Angular . Por ejemplo, tomará la cadena 'some.object.property' y devolverá una función que le permite establecer u obtener un valor en dicha ruta para un determinado contexto. Lo estoy usando para establecer resultados de filtro en el $ alcance aquí, específicamente. Espero que esto responda tu pregunta.
kevinjamesus86
23

Pruebe algo como esto, el problema con ng-repeat es que crea un ámbito secundario debido a que no puede acceder

elementos de filtro

del controlador

<li ng-repeat="doc in $parent.filteritems = (docs | filter:searchitems)" ></li>
imal hasaranga perera
fuente
16

Actualizar:

Incluso después de 1.3.0. si quieres ponerlo en el alcance del controlador o el padre no se puede hacer eso con tan sintaxis. Por ejemplo si tengo el siguiente código:

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

Lo anterior no funcionará. La forma de evitarlo es usando $ parent como sigue :

<li ng-repeat="label in ($parent.labelResults = (labels | filter:query))">
Gal Bracha
fuente
Es bueno tener en cuenta que si tiene un límite para filtrar esto. No se reflejará en $ parent.labelResults
Zack
Si console.log labelResultssiempre obtengo el filtro de datos uno más tarde. No es el resultado del filtro recién cambiado, sino el del cambio anterior. ¿No tuviste este problema?
Anna
10

Se me ocurrió una versión algo mejor de la solución de Andy. En su solución, ng-repeat coloca un reloj en la expresión que contiene la asignación. Cada ciclo de resumen evaluará esa expresión y asignará el resultado a la variable de alcance.

El problema con esta solución es que puede encontrarse con problemas de asignación si está en un ámbito secundario. Esta es la misma razón por la que debería tener un punto en ng-model .

La otra cosa que no me gusta de esta solución es que oculta la definición de la matriz filtrada en algún lugar del marcado de la vista. Si se usa en varios lugares en su vista o en su controlador, puede ser confuso.

Una solución más simple es colocar un reloj en su controlador en una función que realice la asignación:

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

Esta función se evaluará durante cada ciclo de digestión, por lo que el rendimiento debe ser comparable con la solución de Andy. También puede agregar cualquier número de tareas en la función para mantenerlas todas en un solo lugar en lugar de estar dispersas por la vista.

En la vista, simplemente usaría la lista filtrada directamente:

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

Aquí hay un violín donde esto se muestra en un escenario más complicado.

Dave Johnson
fuente
Super limpio y no contamina la estructura, ¡bonito!
kuzyn
esta solución es perfecta para mí ... estaba usando la repetición de colección de ionic en lugar de repetir ng ... esa es la razón por la cual la respuesta aceptada no funcionó para mí
Lakshay