<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Angular2 + Reactive Redux Sketch</title>
    <link rel="stylesheet" href="https://cdn.rawgit.com/tastejs/todomvc-app-css/master/index.css">
    <link rel="stylesheet" href="styles.css">
    <!-- ES6-related imports -->
<script src="http://cdn.rawgit.com/google/traceur-compiler/90da568c7aa8e53ea362db1fc211fbb4f65b5e94/bin/traceur-runtime.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/systemjs/0.18.4/system.js"></script>
<script src="config.js"></script>
<!-- Angular2 import -->
<script src="http://code.angularjs.org/2.0.0-alpha.36/angular2.dev.js"></script>

<script>
    //bootstrap the Angular2 application
    System.import('app').catch(console.log.bind(console));
</script>
</head>

<body>
    <todo-app>Loading...</todo-app> 
</body> 



</html>
import { Component, View, CORE_DIRECTIVES, ON_PUSH } from 'angular2/angular2';
import { RxPipe } from 'lib/rxPipe';
import { NgStore, NG_STORE_BINDINGS } from 'lib/ngStore'

import {TodoAppHeader} from './TodoAppHeader'
import {TodoList} from './TodoList'
import {TodoAppFooter} from './TodoAppFooter'
import Rx, {Observable} from 'rx.all'

@Component({
    selector: 'todo-app',
    changeDetection: ON_PUSH,
    bindings: []
})
@View({
  templateUrl: 'src/TodoApp.html',
  directives: [CORE_DIRECTIVES, TodoAppHeader, TodoList, TodoAppFooter],
  pipes: [RxPipe]
})
export class TodoApp {
  constructor(public todoStore: NgStore){
    
    this.todoFilter = this.todoStore.key('todoFilter');
    this.allTodos = this.todoStore.key('todos')
    
    this.visibleTodos = 
      Observable.combineLatest(this.todoFilter, this.allTodos)
        .map(([todoFilter, todos]) => todos.filter(todoFilter.predicate)))
  }
}
/// https://gist.github.com/jashmenn/d8f5cbf5fc20640bac30

/// <reference path="../../typings/app.d.ts" />
//
//  Creates a pipe suitable for a RxJS observable:
//
//        @View({
//          template: '{{ someObservable | rx}}'
//          pipes: [RxPipe]
//        })
// 
// Originally written by @gdi2290 but updated for 2.0.0.alpha-35 and use AsyncPipe
// (Soon the Angular team will be using RxJS natively and this pipe will be
// unnecessary because we'll be able to use the `async` pipe.)
// 
// References:
// * rxPipeRegistry.ts https://gist.github.com/gdi2290/e9b2880a1d13057197d7 by @gdi2290
// * AsyncPipe https://github.com/angular/angular/blob/master/modules/angular2/src/pipes/async_pipe.ts

import {PipeFactory, Pipe, Injectable, bind, ChangeDetectorRef} from "angular2/angular2";
import {AsyncPipe} from "angular2/pipes";
import * as Rx from 'rx';
import {Observable} from 'rx';

function isObservable(obs) {
  console.log(obs)
  return obs && typeof obs.subscribe === 'function';
}

class RxStrategy {
  createSubscription(async: any, updateLatestValue: any): any {
    return async.subscribe((values) => {
      updateLatestValue(values);
    }, e => { throw e; });
  }
  dispose(subscription: any): void { subscription.dispose(); }
  onDestroy(subscription: any): void { subscription.dispose(); }
}

var _rxStrategy = new RxStrategy();
 
@Pipe({name: 'rx'})
export class RxPipe extends AsyncPipe {
  constructor(public _ref: ChangeDetectorRef) { super(_ref); }

  supports(obs) { return isObservable(obs); }

  _selectStrategy(obj: Observable<any>): any {
    return _rxStrategy;
  }
}
 
export var rxPipeInjectables: Array<any> = [
  bind(RxPipe).toValue(RxPipe)
];
System.config({
        defaultJSExtensions: true,
        transpiler: 'typescript',
        typescriptOptions: {
          emitDecoratorMetadata: true
        },
        map: {
          typescript: 'https://cdn.rawgit.com/robwormald/9883afae87bffa2f4e00/raw/8bca570a696c47a4f8dd03a52c98734676e94cea/typescript.js',
          'rx.all': 'https://cdnjs.cloudflare.com/ajax/libs/rxjs/3.1.1/rx.all.js',
          'redux': 'https://cdnjs.cloudflare.com/ajax/libs/redux/2.0.0/redux.js'
        },
        paths: {
          app: 'src'
        },
        packages: {
          //our app's source code
          app: {
            main: 'main.ts',
            defaultExtension: 'ts',
          },
          lib: {
            defaultExtension: 'ts'
          }
        }
    });
export const ADD_TODO = 'ADD_TODO'
export const REMOVE_TODO = 'REMOVE_TODO'
export const UPDATE_TODO = 'UPDATE_TODO'
export const REMOVE_COMPLETED_TODOS = 'REMOVE_COMPLETED_TODOS'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const TOGGLE_ALL_TODOS = 'TOGGLE_ALL_TODOS'

export const SHOW_ALL_TODOS = 'SHOW_ALL_TODOS'
export const SHOW_COMPLETED_TODOS = 'SHOW_COMPLETED_TODOS'
export const SHOW_ACTIVE_TODOS = 'SHOW_ACTIVE_TODOS'

export const todos = (state = [], action) => {

  switch(action.type){
    case ADD_TODO:
      let todo = Object.assign({},action.payload, {id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1})
      return [...state, todo]
    case REMOVE_TODO:
      return state.filter(todo => todo.id !== action.payload.id)
    case UPDATE_TODO: 
      return state.map(todo => 
        todo.id === action.payload.id ? 
          Object.assign({}, todo, action.payload) : todo)
    case TOGGLE_TODO:
       return state.map(todo => 
         todo.id === action.payload.id ?
         Object.assign({}, todo, action.payload): todo)
    case TOGGLE_ALL_TODOS: 
       return state.map(todo => {
         return Object.assign({},todo, action.payload)
       })
    case REMOVE_COMPLETED_TODOS:
       return state.filter(todo => !todo.completed)
    default:
      return state;
  }
};

const showAll = (item) => true;
const showCompleted = (item) => item.completed;
const showActive = (item) => !item.completed;

export const todoFilter = (state = {predicate: showAll, key: SHOW_ALL_TODOS}, action ) => {

  switch(action.type){
    case SHOW_ALL_TODOS:
      return Object.assign({},{predicate: showAll, key: action.type});
    case SHOW_COMPLETED_TODOS:
      return Object.assign({},{predicate: showCompleted, key: action.type});;
    case SHOW_ACTIVE_TODOS:
      return Object.assign({},{predicate: showActive, key: action.type});;
    default
      return state;
  }
}


import Redux, {
  createStore, combineReducers
}
from 'redux'
import Rx, {
  Observable, Observer, Subject
}
from 'rx.all'
import {
  Injectable
}
from 'angular2/di'

const INIT_STORE = 'INIT_STORE'

@Injectable()
export class NgStoreInitialState {}

@Injectable()
export class NgStoreRootReducer {}

@Injectable()
export class NgStore {
  _subject: Subject<any>;
  constructor(rootReducer: NgStoreRootReducer, initialState: NgStoreInitialState) {

    let store = createStore(rootReducer, initialState);

    const observableStore = Observable.create(changeObserver => {

      let subscriber = store.subscribe(() => changeObserver.onNext(store.getState()));
    
      return () => {
          //TODO: disposal?
          subscriber()
        }
    });

    const actionDispatcher = Observer.create(store.dispatch);

    this._subject = Subject.create(actionDispatcher, observableStore);

    setTimeout(() => {
      store.dispatch({
        action: "NG_STORE_INIT"
      })
    })
  }

  createAction(type: string) {
    return (payload: any) => {
      console.log('dispatching', type, payload)
      this.dispatch({
        type, payload
      })
    };
  }

  dispatch(action) {
    this._subject.onNext(action);
  }

  key(key) {
    return this._subject.map(state => state[key]).publish().refCount();
  }
}

export const NG_STORE_BINDINGS = [NgStoreInitialState, NgStoreRootReducer, NgStore];
### angular2 + redux + rxjs Todo

just a little sketch, working out how to best combine all these shiny new things

- redux provides the (immutable) store
- RxJS subject wraps the store and subscribes to it
- angular2 using ON_PUSH change detection triggered by the Rx pipe (so the store is bound to the view!)
<section class="todoapp">
  <todo-app-header></todo-app-header>
  <todo-list [todos]="visibleTodos"></todo-list>
  <todo-app-footer></todo-app-footer>
</section>
<footer class="info">
  <p>Double-click to edit a todo</p>
  <!-- Remove the below line ↓ -->
  <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
  <!-- Change this out with your name and url ↓ -->
  <p>Created by <a href="http://twitter.com/robwormald" target="_blank">@robwormald</a></p>
  <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
import {
  Component,
  View
} from 'angular2/angular2'

import {
  FormBuilder, 
  Validators,
  FORM_BINDINGS,
  FORM_DIRECTIVES
} from 'angular2/forms';

import {NgStore} from 'lib/ngStore'

@Component({
  selector: 'todo-app-header',
  bindings: [FORM_BINDINGS]
})
@View({
  templateUrl: 'src/TodoAppHeader.html',
  directives: [FORM_DIRECTIVES]
})
export class TodoAppHeader {
  constructor(formBuilder: FormBuilder, todoStore: NgStore){
    this.newTodo = formBuilder.group({
      text: ["", Validators.required],
      completed: false
    });
    this.todoStore = todoStore;
  }
  submit($event){
    $event.preventDefault();
    if(this.newTodo.valid){
      this.todoStore.dispatch({
        type: 'ADD_TODO',
        payload: this.newTodo.value
      });
      this.newTodo.controls.text.updateValue("");
    }
  }
}
//angular imports
import {bootstrap, bind} from 'angular2/angular2'
import {combineReducers} from 'redux'

//import root component
import {TodoApp} from './TodoApp';
import {NgStoreRootReducer, NgStoreInitialState, NG_STORE_BINDINGS} from 'lib/ngStore';
import {todos, todoFilter} from './TodoStore'

bootstrap(TodoApp, [
  NG_STORE_BINDINGS,
  bind(NgStoreRootReducer).toValue(combineReducers({todos, todoFilter})),
  bind(NgStoreInitialState).toValue({
    todos: [
      {
        id: 1,
        text: 'Learn Javascript',
        completed: true
        
      },
      {
        id: 2,
        text: 'Forget Angular1',
        completed: true
        
      },
      {
        id: 3,
        text: 'Use Angular2',
        completed: false
        
      }]
  })
]);
import {
  Component,
  View,
  CORE_DIRECTIVES,
  ON_PUSH
} from 'angular2/angular2'

import {RxPipe} from 'lib/rxPipe'
import {NgStore} from 'lib/ngStore'

import {TodoItem} from './TodoItem'

@Component({
  selector: 'todo-list',
  bindings: [],
  properties: ['todos'],
  changeDetection: ON_PUSH
})
@View({
  templateUrl: 'src/TodoList.html',
  directives: [CORE_DIRECTIVES, TodoItem],
  pipes: [RxPipe]
})
export class TodoList {
  constructor(private todoStore: NgStore){}
  toggleAll($event){
    this.todoStore.dispatch({
      type: 'TOGGLE_ALL_TODOS',
      payload: {
        completed: $event.target.checked
      }
    })
  }
  
}
<section class="main">
  <input class="toggle-all" type="checkbox" (change)="toggleAll($event)">
  <label for="toggle-all">Mark all as complete</label>
  <ul class="todo-list">
    <todo-item *ng-for="#todo of todos | rx" [todo]="todo"></todo-item>
  </ul>
</section>
import {
  Component,
  View,
  CORE_DIRECTIVES,
  ON_PUSH,
  LifecycleEvent
} from 'angular2/angular2'

import {
  FORM_DIRECTIVES,
  FORM_BINDINGS,
  FormBuilder,
  Validators
} from 'angular2/forms'

import {RxPipe} from 'lib/rxPipe'

import {NgStore} from 'lib/ngStore'

@Component({
  selector: 'todo-item',
  bindings: [FORM_BINDINGS],
  properties: ['todo'],
  changeDetection: ON_PUSH
})
@View({
  templateUrl: 'src/TodoItem.html',
  directives: [CORE_DIRECTIVES],
  pipes: [RxPipe]
})
export class TodoItem {
  constructor(private todoStore: NgStore){

  }
  
  destroy(){
    this.todoStore.dispatch({
      type: 'REMOVE_TODO',
      payload: this.todo
    });
  }
  
  updateTodo($event,editableTodo){
    $event.preventDefault()
    this.todoStore.dispatch({
      type: 'UPDATE_TODO',
      payload: {
        text: editableTodo.value,
        id: this.todo.id
      }
    });
    this.toggleEditable()
  }
  
  toggleEditable(){
    
    this.todoStore.dispatch({
      type: 'UPDATE_TODO',
      payload: {id: this.todo.id, editing: !this.todo.editing}
    });
  }
  
  toggleCompleted($event){
    this.todoStore.dispatch({
      type: 'UPDATE_TODO',
      payload: {id: this.todo.id, completed: $event.target.checked}
    });
  }
  
}
<li [ng-class]="todo">
  <div class="view">
    <input class="toggle" type="checkbox" (change)="toggleCompleted($event)" [checked]="todo.completed">
    <label (dblclick)="toggleEditable($event)">{{todo.text}}</label>
    <button class="destroy" (click)="destroy()"></button>
  </div>
  <form (^submit)="updateTodo($event, editabletodo)">
    <input #editabletodo class="edit" [value]="todo.text">
  </form>
</li>
<section class="header">
  <form (^submit)="submit($event)" [ng-form-model]="newTodo">
    <h1>ng2dos</h1>
    <input class="new-todo" placeholder="What needs to be done?" ng-control="text" autofocus>
  </form>
</section>
import {
  Component,
  View,
  CORE_DIRECTIVES,
  ON_PUSH
} from 'angular2/angular2'

import {NgStore} from 'lib/ngStore'
import {RxPipe} from 'lib/rxPipe'


@Component({
  selector: 'todo-app-footer',
  bindings: [],
  changeDetection: ON_PUSH
})
@View({
  templateUrl: 'src/TodoAppFooter.html',
  directives: [CORE_DIRECTIVES],
  pipes: [RxPipe]
})
export class TodoAppFooter {
  constructor(private todoStore: NgStore){
    
    this.remainingTodosCount = 
      this.todoStore.key('todos')
        .map(todos => todos.filter(todo => !todo.completed))
        .map(remainingTodos => remainingTodos.length);
    
    this.todoStore.key('todoFilter')
      .map(todoFilter => todoFilter.key)
      .subscribe(key => this.currentFilterKey = key);
  }
  showAllTodos(){
    this.todoStore.dispatch({
      type: 'SHOW_ALL_TODOS'
    })
  }
  showActiveTodos(){
    this.todoStore.dispatch({
      type: 'SHOW_ACTIVE_TODOS'
    })
  }
  showCompletedTodos(){
    this.todoStore.dispatch({
      type: 'SHOW_COMPLETED_TODOS'
    })
  }
  clearCompletedTodos(){
    this.todoStore.dispatch({
      type: 'REMOVE_COMPLETED_TODOS'
    })
  }
  
}
<footer class="footer">
  <span class="todo-count"><strong>{{remainingTodosCount | rx}}</strong> items left</span>
  <!-- Remove this if you don't implement routing -->
  <ul class="filters">
    <li>
      <a [ng-class]="{selected: currentFilterKey === 'SHOW_ALL_TODOS'}" href="#" (click)="showAllTodos()">All</a>
    </li>
    <li>
      <a [ng-class]="{selected: currentFilterKey === 'SHOW_ACTIVE_TODOS'}" href="#" (click)="showActiveTodos()">Active</a>
    </li>
    <li>
      <a [ng-class]="{selected: currentFilterKey === 'SHOW_COMPLETED_TODOS'}" href="#" (click)="showCompletedTodos()">Completed</a>
    </li>
  </ul>

  <button class="clear-completed" (click)="clearCompletedTodos()">Clear completed</button>
</footer>