How to make a sorting pipe in angular2 with an array of objects
Original Problem:
I have a TODOs list, (Todo[ ]) and I want to sort it every time I make some changes. I want that the completed todo are displayed at the bottom of the list. The Todo object has a property named .completed that stores a boolean value, it will tell us if the todo is completed or not.
Creating the Pipe:
In Angular2 the "OrderBy" pipe does not exist. So we have to build it:
import { Pipe, PipeTransform } from "angular2/core";
//Todo is the interface for our todo object
import {Todo} from './todo';
@Pipe({
name: "sort",
//set to false so it will always update, read below the code.
pure: false
})
export class TodosSortPipe implements PipeTransform {
transform(array: Todo[], args: any): Todo[] {
//watch the console to see how many times you pipe is called
console.log("calling pipe");
/* javascript is async, so could be that the pipe is called before
that the todos list is created, in this case we do nothing
returning the array as it is */
if (isBlank(array)) return null;
array.sort((a, b) => {
if (a.completed < b.completed) {
return -1;
//.completed because we want to sort the list by completed property
} else if (a.completed > b.completed) {
return 1;
} else {
return 0;
}
});
return array;
}
}
If you didn't understand the sort method check MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
The Pipe is done, let's move to the Component.
Creating the Component:
The AppComponent class, creates an array of Todo, called Todos, getting the objects from a mock with a service.
import {Component, OnInit} from 'angular2/core';
import {Todo} from './todo';
import {TodoService} from './todo.service';
import {TodosSortPipe} from './sort-pipe.component'
@Component({
//name of the html element
selector: 'my-app',
//view of the selector, " ` " is alt + 9
templateUrl: "./app/todo-list.component.html",
providers: [TodoService],
pipes: [ TodosSortPipe ]
})
export class AppComponent implements OnInit{
public todos: Todo[];
public edited = false;
public changes = 0;
//creating an istance of todoService
constructor(private _todoService: TodoService) { };
//getting the todos list from the service
getTodos() {
this._todoService.getTodos().then(todos => this.todos = todos);
}
(...)
editTodo(todo: Todo): void {
//slice is very important, read below the code
this.todos = this.todos.slice();
this.saveTodos();
}
}
Template implementation
This is the pipe calling:
<li *ngFor="#todo of todos | sort; #i=index">
(...)
</li>
Demo:
For the full example with all the code: https://plnkr.co/edit/VICRMVNhqdqK9V4rJZYm?p=preview Watch it on github: https://github.com/AndreaMiotto/Angular2-TodoApp
Pipe Unpure
Pipes only change by default when your Pipe input parameters change and not when your data changes. Setting Pure to false, you will make it "unpure" so you pipe will always update.