angular - orderby - Pedido Por problema de tubería
pipe order by angular 4 (16)
No puedo traducir este código de Angualr 1 a Angular 2:
ng-repeat="todo in todos | orderBy: ''completed''"
Esto es lo que he hecho siguiendo la respuesta de Thierry Templier:
Plantilla de componentes:
*ngFor="#todo of todos | sort"
Código de componente:
@Component({
selector: ''my-app'',
templateUrl: "./app/todo-list.component.html",
providers: [TodoService],
pipes: [ TodosSortPipe ]
})
Código de tubería:
import { Pipe } from "angular2/core";
import {Todo} from ''./todo'';
@Pipe({
name: "sort"
})
export class TodosSortPipe {
transform(array: Array<Todo>, args: string): Array<Todo> {
array.sort((a: any, b: any) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return array;
}
}
Estoy tratando de ordenar una matriz de
Todo
s, ordenada por la propiedad
completed
.
Primero
todo.completed = false
y luego
todo.complete = true
.
No entiendo muy bien el método de
transform
y cómo pasar los argumentos en ese método y en el método de
sort
.
¿Cuál es el argumento
args: string
?
¿Qué son
b
y de dónde vienen?
Angular no viene con un pedido. Por el filtro listo para usar, pero si decidimos que necesitamos uno, podemos hacer uno fácilmente. Sin embargo, debemos tener en cuenta algunas advertencias relacionadas con la velocidad y la minificación. Vea abajo.
Una tubería simple se vería así.
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''sort''
})
export class SortPipe implements PipeTransform {
transform(ary: any, fn: Function = (a,b) => a > b ? 1 : -1): any {
return ary.sort(fn)
}
}
Esta tubería acepta una función de clasificación (
fn
) y le da un valor predeterminado que clasificará una matriz de primitivas de manera sensata.
Tenemos la opción de anular esta función de ordenación si lo deseamos.
No acepta un nombre de atributo como una cadena, porque los nombres de atributo están sujetos a minificación. Cambiarán cuando minimicemos nuestro código, pero los minificadores no son lo suficientemente inteligentes como para minimizar también el valor en la cadena de la plantilla.
Clasificación de primitivas (números y cadenas)
Podríamos usar esto para ordenar una matriz de números o cadenas usando el comparador predeterminado:
import { Component } from ''@angular/core'';
@Component({
selector: ''cat'',
template: `
{{numbers | sort}}
{{strings | sort}}
`
})
export class CatComponent
numbers:Array<number> = [1,7,5,6]
stringsArray<string> = [''cats'', ''hats'', ''caveats'']
}
Ordenar una matriz de objetos
Si queremos ordenar una matriz de objetos, podemos asignarle una función de comparación.
import { Component } from ''@angular/core'';
@Component({
selector: ''cat'',
template: `
{{cats | sort:byName}}
`
})
export class CatComponent
cats:Array<Cat> = [
{name: "Missy"},
{name: "Squoodles"},
{name: "Madame Pompadomme"}
]
byName(a,b) {
return a.name > b.name ? 1 : -1
}
}
Advertencias: tubos puros versus impuros
Angular 2 tiene un concepto de tuberías puras e impuras.
Una tubería pura
optimiza la detección de cambios utilizando la identidad del objeto
Esto significa que la tubería solo se ejecutará si el objeto de entrada cambia de identidad, por ejemplo, si agregamos un nuevo elemento a la matriz.
No descenderá a objetos.
Esto significa que si cambiamos un atributo anidado:
this.cats[2].name = "Fluffy"
por ejemplo, la tubería no se volverá a ejecutar.
Esto ayuda a Angular a ser rápido.
Las tuberías angulares son puras por defecto.
Una tubería impura, por otro lado, verificará los atributos del objeto. Potencialmente, esto lo hace mucho más lento. Debido a que no puede garantizar lo que hará la función de tubería (tal vez se clasificó de manera diferente según la hora del día, por ejemplo), se ejecutará una tubería impura cada vez que ocurra un evento asincrónico. Esto ralentizará su aplicación considerablemente si la matriz es grande.
El tubo de arriba es puro. Esto significa que solo se ejecutará cuando los objetos de la matriz sean inmutables. Si cambia un gato, debe reemplazar todo el objeto del gato por uno nuevo.
this.cats[2] = {name:"Tomy"}
Podemos cambiar lo anterior a una tubería impura estableciendo el atributo puro:
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''sort'',
pure: false
})
export class SortPipe implements PipeTransform {
transform(ary: any, fn: Function = (a,b) => a > b ? 1 : -1): any {
return ary.sort(fn)
}
}
Esta tubería descenderá a objetos, pero será más lenta. Usar con precaución.
Como sabemos que el filtro y el orden se eliminan de ANGULAR 2 y necesitamos escribir el nuestro, aquí hay un buen ejemplo de plunker y artículo detallado
Usó tanto el filtro como el pedido, aquí está el código para la tubería de pedido
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({ name: ''orderBy'' })
export class OrderrByPipe implements PipeTransform {
transform(records: Array<any>, args?: any): any {
return records.sort(function(a, b){
if(a[args.property] < b[args.property]){
return -1 * args.direction;
}
else if( a[args.property] > b[args.property]){
return 1 * args.direction;
}
else{
return 0;
}
});
};
}
Consulte https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe para ver la discusión completa. Esta cita es la más relevante. Básicamente, para las aplicaciones a gran escala que deben minimizarse agresivamente, la lógica de filtrado y clasificación debe pasar al componente en sí.
"Es posible que a algunos de nosotros no nos interese minificar esto agresivamente. Esa es nuestra elección. Pero el producto Angular no debe evitar que alguien minifique agresivamente. Por lo tanto, el equipo de Angular decidió que todo lo que se envíe en Angular se reducirá de manera segura".
El equipo de Angular y muchos desarrolladores angulares experimentados recomiendan encarecidamente que mueva la lógica de filtrado y clasificación al componente mismo. El componente puede exponer una propiedad de Héroes filtrados o Héroes ordenados y tomar el control sobre cuándo y con qué frecuencia ejecutar la lógica de soporte. Cualquier capacidad que hubiera puesto en una tubería y compartida en la aplicación puede escribirse en un servicio de filtrado / clasificación e inyectarse en el componente ".
En la versión actual de Angular2, las tuberías orderBy y ArraySort no son compatibles. Necesita escribir / usar algunos tubos personalizados para esto.
En package.json, agregue algo como (Esta versión está bien para Angular 2):
"ngx-order-pipe": "^1.1.3",
En su módulo de mecanografía (y matriz de importaciones):
import { OrderModule } from ''ngx-order-pipe'';
Este es un buen reemplazo para la tubería de pedido AngularJs en angular 4 . Fácil y simple de usar.
Esta es la URL de Github para obtener más información https://github.com/VadimDez/ngx-order-pipe
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''orderBy''
})
export class OrderPipe implements PipeTransform {
transform(value: any | any[], expression?: any, reverse?: boolean): any {
if (!value) {
return value;
}
const isArray = value instanceof Array;
if (isArray) {
return this.sortArray(value, expression, reverse);
}
if (typeof value === ''object'') {
return this.transformObject(value, expression, reverse);
}
return value;
}
/**
* Sort array
*
* @param value
* @param expression
* @param reverse
* @returns {any[]}
*/
private sortArray(value: any[], expression?: any, reverse?: boolean): any[] {
const isDeepLink = expression && expression.indexOf(''.'') !== -1;
if (isDeepLink) {
expression = OrderPipe.parseExpression(expression);
}
let array: any[] = value.sort((a: any, b: any): number => {
if (!expression) {
return a > b ? 1 : -1;
}
if (!isDeepLink) {
return a[expression] > b[expression] ? 1 : -1;
}
return OrderPipe.getValue(a, expression) > OrderPipe.getValue(b, expression) ? 1 : -1;
});
if (reverse) {
return array.reverse();
}
return array;
}
/**
* Transform Object
*
* @param value
* @param expression
* @param reverse
* @returns {any[]}
*/
private transformObject(value: any | any[], expression?: any, reverse?: boolean): any {
let parsedExpression = OrderPipe.parseExpression(expression);
let lastPredicate = parsedExpression.pop();
let oldValue = OrderPipe.getValue(value, parsedExpression);
if (!(oldValue instanceof Array)) {
parsedExpression.push(lastPredicate);
lastPredicate = null;
oldValue = OrderPipe.getValue(value, parsedExpression);
}
if (!oldValue) {
return value;
}
const newValue = this.transform(oldValue, lastPredicate, reverse);
OrderPipe.setValue(value, newValue, parsedExpression);
return value;
}
/**
* Parse expression, split into items
* @param expression
* @returns {string[]}
*/
private static parseExpression(expression: string): string[] {
expression = expression.replace(//[(/w+)/]/g, ''.$1'');
expression = expression.replace(/^/./, '''');
return expression.split(''.'');
}
/**
* Get value by expression
*
* @param object
* @param expression
* @returns {any}
*/
private static getValue(object: any, expression: string[]) {
for (let i = 0, n = expression.length; i < n; ++i) {
const k = expression[i];
if (!(k in object)) {
return;
}
object = object[k];
}
return object;
}
/**
* Set value by expression
*
* @param object
* @param value
* @param expression
*/
private static setValue(object: any, value: any, expression: string[]) {
let i;
for (i = 0; i < expression.length - 1; i++) {
object = object[expression[i]];
}
object[expression[i]] = value;
}
}
Esto funcionará para cualquier campo que le pase. ( IMPORTANTE: solo se ordenará alfabéticamente, por lo que si pasa una fecha, lo ordenará como alfabeto, no como fecha)
/*
* Example use
* Basic Array of single type: *ngFor="let todo of todoService.todos | orderBy : ''-''"
* Multidimensional Array Sort on single column: *ngFor="let todo of todoService.todos | orderBy : [''-status'']"
* Multidimensional Array Sort on multiple columns: *ngFor="let todo of todoService.todos | orderBy : [''status'', ''-title'']"
*/
import {Pipe, PipeTransform} from "@angular/core";
@Pipe({name: "orderBy", pure: false})
export class OrderByPipe implements PipeTransform {
value: string[] = [];
static _orderByComparator(a: any, b: any): number {
if (a === null || typeof a === "undefined") { a = 0; }
if (b === null || typeof b === "undefined") { b = 0; }
if (
(isNaN(parseFloat(a)) ||
!isFinite(a)) ||
(isNaN(parseFloat(b)) || !isFinite(b))
) {
// Isn"t a number so lowercase the string to properly compare
a = a.toString();
b = b.toString();
if (a.toLowerCase() < b.toLowerCase()) { return -1; }
if (a.toLowerCase() > b.toLowerCase()) { return 1; }
} else {
// Parse strings as numbers to compare properly
if (parseFloat(a) < parseFloat(b)) { return -1; }
if (parseFloat(a) > parseFloat(b)) { return 1; }
}
return 0; // equal each other
}
public transform(input: any, config = "+"): any {
if (!input) { return input; }
// make a copy of the input"s reference
this.value = [...input];
let value = this.value;
if (!Array.isArray(value)) { return value; }
if (!Array.isArray(config) || (Array.isArray(config) && config.length === 1)) {
let propertyToCheck: string = !Array.isArray(config) ? config : config[0];
let desc = propertyToCheck.substr(0, 1) === "-";
// Basic array
if (!propertyToCheck || propertyToCheck === "-" || propertyToCheck === "+") {
return !desc ? value.sort() : value.sort().reverse();
} else {
let property: string = propertyToCheck.substr(0, 1) === "+" || propertyToCheck.substr(0, 1) === "-"
? propertyToCheck.substr(1)
: propertyToCheck;
return value.sort(function(a: any, b: any) {
let aValue = a[property];
let bValue = b[property];
let propertySplit = property.split(".");
if (typeof aValue === "undefined" && typeof bValue === "undefined" && propertySplit.length > 1) {
aValue = a;
bValue = b;
for (let j = 0; j < propertySplit.length; j++) {
aValue = aValue[propertySplit[j]];
bValue = bValue[propertySplit[j]];
}
}
return !desc
? OrderByPipe._orderByComparator(aValue, bValue)
: -OrderByPipe._orderByComparator(aValue, bValue);
});
}
} else {
// Loop over property of the array in order and sort
return value.sort(function(a: any, b: any) {
for (let i = 0; i < config.length; i++) {
let desc = config[i].substr(0, 1) === "-";
let property = config[i].substr(0, 1) === "+" || config[i].substr(0, 1) === "-"
? config[i].substr(1)
: config[i];
let aValue = a[property];
let bValue = b[property];
let propertySplit = property.split(".");
if (typeof aValue === "undefined" && typeof bValue === "undefined" && propertySplit.length > 1) {
aValue = a;
bValue = b;
for (let j = 0; j < propertySplit.length; j++) {
aValue = aValue[propertySplit[j]];
bValue = bValue[propertySplit[j]];
}
}
let comparison = !desc
? OrderByPipe._orderByComparator(aValue, bValue)
: -OrderByPipe._orderByComparator(aValue, bValue);
// Don"t return 0 yet in case of needing to sort by next property
if (comparison !== 0) { return comparison; }
}
return 0; // equal each other
});
}
}
}
He creado una tubería OrderBy que hace justo lo que necesita. Es compatible con la posibilidad de ordenar en varias columnas de un número de objetos enumerables también.
<li *ngFor="#todo in todos | orderBy : [''completed'']">{{todo.name}} {{todo.completed}}</li>
Esta tubería permite agregar más elementos a la matriz después de renderizar la página, y ordenará la matriz con las actualizaciones dinámicamente.
Tengo un artículo sobre el proceso aquí .
Y aquí hay una demostración en funcionamiento: http://fuelinteractive.github.io/fuel-ui/#/pipe/orderby y https://plnkr.co/edit/DHLVc0?p=info
Modifiqué la respuesta de @Thierry Templier para que la tubería pueda ordenar objetos personalizados en angular 4:
import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
name: "sort"
})
export class ArraySortPipe implements PipeTransform {
transform(array: any, field: string): any[] {
if (!Array.isArray(array)) {
return;
}
array.sort((a: any, b: any) => {
if (a[field] < b[field]) {
return -1;
} else if (a[field] > b[field]) {
return 1;
} else {
return 0;
}
});
return array;
}
}
Y para usarlo:
*ngFor="let myObj of myArr | sort:''fieldName''"
Esperemos que esto ayude a alguien.
Puede implementar una tubería personalizada para esto que aproveche el método de
sort
de matrices:
import { Pipe } from "angular2/core";
@Pipe({
name: "sort"
})
export class ArraySortPipe {
transform(array: Array<string>, args: string): Array<string> {
array.sort((a: any, b: any) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return array;
}
}
Y luego use este tubo como se describe a continuación.
No olvide especificar su tubería en el atributo de
pipes
del componente:
@Component({
(...)
template: `
<li *ngFor="list | sort"> (...) </li>
`,
pipes: [ ArraySortPipe ]
})
(...)
Es una muestra simple para matrices con valores de cadena pero puede tener un procesamiento de clasificación avanzado (basado en atributos de objeto en el caso de matriz de objetos, basado en parámetros de clasificación, ...).
Aquí hay un plunkr para esto: https://plnkr.co/edit/WbzqDDOqN1oAhvqMkQRQ?p=preview .
Espero que te ayude, Thierry
Puede usar esto para objetos:
@Pipe({
name: ''sort'',
})
export class SortPipe implements PipeTransform {
transform(array: any[], field: string): any[] {
return array.sort((a, b) => a[field].toLowerCase() !== b[field].toLowerCase() ? a[field].toLowerCase() < b[field].toLowerCase() ? -1 : 1 : 0);
}
}
Te recomiendo que uses lodash con angular, luego tu pipa será la siguiente:
import {Pipe, PipeTransform} from ''@angular/core'';
import * as _ from ''lodash''
@Pipe({
name: ''orderBy''
})
export class OrderByPipe implements PipeTransform {
transform(array: Array<any>, args?: any): any {
return _.sortBy(array, [args]);
}
}
y úsalo en html como
*ngFor = "#todo of todos | orderBy:''completed''"
y no olvides agregar Pipe a tu módulo
@NgModule({
...,
declarations: [OrderByPipe, ...],
...
})
orderby Pipe en Angular JS admitirá pero Angular (versiones superiores) no lo admitirá . Encuentre los detalles discutidos para aumentar la velocidad de rendimiento es obsoleto.
https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
OrderByPipe actualizado: arreglado no ordenando cadenas.
crear una clase OrderByPipe:
import { Pipe, PipeTransform } from "@angular/core";
@Pipe( {
name: ''orderBy''
} )
export class OrderByPipe implements PipeTransform {
transform( array: Array<any>, orderField: string, orderType: boolean ): Array<string> {
array.sort( ( a: any, b: any ) => {
let ae = a[ orderField ];
let be = b[ orderField ];
if ( ae == undefined && be == undefined ) return 0;
if ( ae == undefined && be != undefined ) return orderType ? 1 : -1;
if ( ae != undefined && be == undefined ) return orderType ? -1 : 1;
if ( ae == be ) return 0;
return orderType ? (ae.toString().toLowerCase() > be.toString().toLowerCase() ? -1 : 1) : (be.toString().toLowerCase() > ae.toString().toLowerCase() ? -1 : 1);
} );
return array;
}
}
en su controlador:
@Component({
pipes: [OrderByPipe]
})
o en tu
declarations: [OrderByPipe]
en tu html:
<tr *ngFor="let obj of objects | orderBy : ObjFieldName: OrderByType">
ObjFieldName: nombre del campo del objeto que desea ordenar;
OrderByType: boolean; verdadero: orden descendente; falso: ascendente;
Para la versión Angular 5+ podemos usar el paquete ngx-order-pipe
Paquete de instalación
$ npm install ngx-order-pipe --save
Importar en módulo de aplicaciones
import { BrowserModule } from ''@angular/platform-browser'';
import { NgModule } from ''@angular/core'';
import { FormsModule } from ''@angular/forms'';
import { AppComponent } from ''./app.component'';
import { OrderModule } from ''ngx-order-pipe'';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
OrderModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
usar en cualquier lugar
<ul>
<li *ngFor="let item of (dummyData | orderBy:''name'') ">
{{item.name}}
</li>
</ul>
Component template:
todos| sort: ‘property’:’asc|desc’
Pipe code:
import { Pipe,PipeTransform } from "angular/core";
import {Todo} from ''./todo'';
@Pipe({
name: "sort"
})
export class TodosSortPipe implements PipeTransform {
transform(array: Array<Todo>, args: string): Array<Todo> {
array.sort((a: any, b: any) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {`enter code here`
return 0;
}
});
return array;
}
}