Skip to content

Instantly share code, notes, and snippets.

@btroncone
Last active February 20, 2019 19:48
Show Gist options
  • Save btroncone/124234d169b808d6db5b to your computer and use it in GitHub Desktop.
Save btroncone/124234d169b808d6db5b to your computer and use it in GitHub Desktop.
Ng2 Observable Todo-Service
import {Component, Input, Output, EventEmitter} from "angular2/core";
import {Todo} from "../services/Todo-Service";
@Component({
selector: 'todo-item',
styles: [
`
.complete{
text-decoration: line-through;
}
`
],
template: `
<section class="post">
<header class="post-header">
<h2 class="post-title" [class.complete]="todo.complete">{{todo.description}}</h2>
<p class="post-meta">
<button class="post-category post-category-design" (click)="toggleTodo.emit(todo)">Toggle Status</button>
<button class="post-category post-category-js" (click)="deleteTodo.emit(todo)">Delete</button>
</p>
</header>
</section>
`
})
export class TodoItem{
@Input() todo : Todo;
@Output() toggleTodo = new EventEmitter<Todo>();
@Output() deleteTodo = new EventEmitter<Todo>();
}
import {Component, ChangeDetectionStrategy} from "angular2/core";
import {TodoService, Todo} from "../services/Todo-Service";
import {TodoItem} from "./../components/todo-item";
import {AsyncPipe, AbstractControl, ControlGroup, FormBuilder, Validators} from "angular2/common";
@Component({
selector: 'todo-list',
providers: [TodoService],
template: `
<h1 class="content-subhead">Todo List</h1>
<form [ngFormModel]="todoForm"
(ngSubmit)="addTodo(todoForm.value)">
<input type="text" #todoInput [ngFormControl]="todoDescription"/>
<div [hidden]="todoForm.valid || !todoForm.dirty">
Todo Description is Required
</div>
<button type="submit" [disabled]="!todoForm.valid"> Add Todo</button>
</form>
<todo-item
*ngFor="#todo of todoService.todos | async"
[todo]="todo"
(deleteTodo)="todoService.deleteTodo($event)"
(toggleTodo)="todoService.toggleTodo($event)">
</todo-item>
`,
directives: [TodoItem],
pipes: [AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoList{
todoForm : ControlGroup;
todoDescription: AbstractControl;
constructor(private todoService : TodoService, private fb : FormBuilder){
this.todoForm = fb.group({
'todoDescription': ['', Validators.required]
});
this.todoDescription = this.todoForm.controls['todoDescription'];
}
addTodo(form){
const { todoDescription } = form;
this.todoService.createTodo({
description: todoDescription,
complete: false
});
this.todoDescription.updateValue('');
}
}
import {Injectable} from "angular2/core";
import {Subject} from "rxjs/Subject";
import {ReplaySubject} from "rxjs/Rx";
export interface Todo{
description: string,
complete: boolean
}
export interface TodoOperation{
(todos : Todo[] | any): Todo[];
}
@Injectable()
export class TodoService{
public todos: ReplaySubject<Todo[]> = new ReplaySubject<Todo[]>();
private updates: Subject<TodoOperation> = new Subject<TodoOperation>();
private create: Subject<Todo> = new Subject<Todo>();
private delete: Subject<Todo> = new Subject<Todo>();
private toggleStatus: Subject<Todo> = new Subject<Todo>();
constructor(){
this.updates
.scan((todos : Todo[], operation: TodoOperation) => {
return operation(todos);
}, [])
.subscribe(this.todos);
this.create
.map((todo : Todo) => (state : Todo[]) => {
return [
...state,
todo
];
})
.subscribe(this.updates);
this.delete
.map((todo : Todo) => (state: Todo[]) => {
const todoIndex = state.indexOf(todo);
return[
...state.slice(0, todoIndex),
...state.slice(todoIndex + 1)
]
})
.subscribe(this.updates);
this.toggleStatus
.map((todo: Todo) => (state: Todo[]) => {
return state.map((todoItem : Todo) => {
if(todoItem === todo){
todoItem.complete = !todoItem.complete;
}
return todoItem;
});
})
.subscribe(this.updates);
}
createTodo(todo: Todo){
this.create.next(todo);
}
deleteTodo(todo: Todo){
this.delete.next(todo);
}
toggleTodo(todo: Todo){
this.toggleStatus.next(todo);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment