Skip to content

Commit

Permalink
feat: todos logic
Browse files Browse the repository at this point in the history
  • Loading branch information
tomastrajan committed May 21, 2017
1 parent 878662e commit 2ba208c
Show file tree
Hide file tree
Showing 21 changed files with 215 additions and 51 deletions.
5 changes: 3 additions & 2 deletions src/app/app.component.scss-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
}
}
}

.footer {
color: mat-color($primary, A700);
color: mat-color($primary, lighter);
background-color: mat-color($primary);

.links {
a {
color: mat-color($primary, A700);
color: mat-color($primary, lighter);
&:hover {
color: mat-color($accent);
}
Expand Down
3 changes: 1 addition & 2 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ export function reducerAoT(state, action) {
@NgModule({
imports: [
CommonModule,
StoreModule.provideStore(reducerAoT),
EffectsModule.run(SettingsEffects)
StoreModule.provideStore(reducerAoT)
],
declarations: [],
providers: [LocalStorageService]
Expand Down
5 changes: 4 additions & 1 deletion src/app/examples/examples.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { Store } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';

import { CoreModule, createReducer } from '../core';
import { SharedModule } from '../shared';
Expand All @@ -11,6 +12,7 @@ import { StockMarketComponent } from './stock-market/stock-market.component';
import { StockMarketService } from './stock-market/stock-market.service';

import { todosReducer } from './todos/todos.reducer';
import { TodosEffects } from './todos/todos.effects';

export const appReducerWithExamples = createReducer({
todos: todosReducer
Expand All @@ -20,7 +22,8 @@ export const appReducerWithExamples = createReducer({
imports: [
CoreModule,
SharedModule,
ExamplesRoutingModule
ExamplesRoutingModule,
EffectsModule.run(TodosEffects)
],
declarations: [TodosComponent, ExamplesComponent, StockMarketComponent],
providers: [StockMarketService]
Expand Down
48 changes: 40 additions & 8 deletions src/app/examples/todos/todos.component.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
<div class="container">
<div class="row">
<div class="offset-md-2 col-md-8 entry">
<anms-big-input placeholder="I want to do..."
<anms-big-input placeholder="I am going to do..."
[value]="newTodo"
(keyup)="onNewTodoChange($event.target.value)"
(keyup.enter)="onAddTodo()">
(keyup.enter)="!isAddTodoDisabled && onAddTodo()">
<anms-big-input-action icon="add" color="accent"
(click)="onAddTodo()"
[disabled]="newTodo.length < 4">
(action)="onAddTodo()"
[disabled]="isAddTodoDisabled">
</anms-big-input-action>
<anms-big-input-action icon="delete_forever"
(action)="onRemoveDoneTodos()"
[disabled]="isRemoveDoneTodosDisabled">
</anms-big-input-action>
<anms-big-input-action icon="delete_forever" disabled></anms-big-input-action>
</anms-big-input>
</div>
</div>
<div class="row">
<div class="col-md-6">
<h2>Todo List</h2>
<md-card *ngFor="let todo of todos.items" class="todo">
{{todo.name}}
<h2>
Todo List
<button class="todos-filter" md-icon-button [mdMenuTriggerFor]="todosFilter">
<md-icon>filter_list</md-icon>
</button>
<md-menu class="todos-filter-menu" #todosFilter="mdMenu" xPosition="before">
<button md-menu-item (click)="onFilterTodos('ALL')" [ngClass]="{ active: todos.filter === 'ALL' }">
<md-icon>assignment</md-icon>
<span>All</span>
</button>
<button md-menu-item (click)="onFilterTodos('DONE')" [ngClass]="{ active: todos.filter === 'DONE' }">
<md-icon>done</md-icon>
<span>Done</span>
</button>
<button md-menu-item (click)="onFilterTodos('ACTIVE')" [ngClass]="{ active: todos.filter === 'ACTIVE' }">
<md-icon>check_box_outline_blank</md-icon>
<span>Active</span>
</button>
</md-menu>
<md-chip-list class="todos-filter-info">
<md-chip>
Displaying {{todos.filter !== 'ALL' ? filteredTodos.length : ''}}
{{todos.filter.toLowerCase()}}
{{todos.filter === 'ALL' ? filteredTodos.length : ''}}
todo{{filteredTodos.length > 1 ? 's' : ''}}
</md-chip>
</md-chip-list>
</h2>
<md-card *ngFor="let todo of filteredTodos" class="todo">
<md-checkbox class="todo-done" [checked]="todo.done" (change)="onToggleTodo(todo)"></md-checkbox>
<span class="todo-label" (click)="onToggleTodo(todo)">{{todo.name}}</span>
</md-card>
<br>
<br>
</div>
<div class="offset-md-1 col-md-5">
<h2>Todo Example</h2>
Expand Down
24 changes: 24 additions & 0 deletions src/app/examples/todos/todos.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@
margin-bottom: 40px;
}

.todos-filter-info {
float: right;
font-weight: normal;
}

.todos-filter {
float: right;
position: relative;
left: 10px;
top: -5px;
margin-left: -10px;
}

.todo {
display: flex;
margin-bottom: 10px;

.todo-done {
margin: 0 20px 0 0;
}

.todo-label {
position: relative;
top: 2px;
cursor: pointer;
}
}
18 changes: 18 additions & 0 deletions src/app/examples/todos/todos.component.scss-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@import '~@angular/material/theming';

@mixin todos-component-theme($theme) {
$accent: map-get($theme, accent);

.todos-filter-menu {
.active {
color: mat-color($accent, default-contrast);
background-color: mat-color($accent);

&:hover {
color: mat-color($accent, default-contrast);
background-color: mat-color($accent, darker);
}
}
}
}

45 changes: 38 additions & 7 deletions src/app/examples/todos/todos.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/map';

import { addTodo } from './todos.reducer';
import {
addTodo, persistTodos, toggleTodo, removeDoneTodos, Todo, filterTodos,
TodoFilter
} from './todos.reducer';

@Component({
selector: 'anms-todos',
Expand All @@ -18,11 +21,6 @@ export class TodosComponent implements OnInit, OnDestroy {
todos: any;
newTodo = '';

inputActions = [
{ type: 'add', icon: 'add', color: 'accent'},
{ type: 'remove', icon: 'delete_forever'}
];

constructor(
public store: Store<any>
) {}
Expand All @@ -31,7 +29,10 @@ export class TodosComponent implements OnInit, OnDestroy {
this.store
.select('todos')
.takeUntil(this.unsubscribe$)
.subscribe(todos => (this.todos = todos));
.subscribe(todos => {
this.todos = todos;
this.store.dispatch(persistTodos(todos));
});
}


Expand All @@ -40,6 +41,24 @@ export class TodosComponent implements OnInit, OnDestroy {
this.unsubscribe$.complete();
}

get filteredTodos() {
const filter = this.todos.filter;
if (filter === 'ALL') {
return this.todos.items;
} else {
const predicate = filter === 'DONE' ? t => t.done : t => !t.done;
return this.todos.items.filter(predicate);
}
}

get isAddTodoDisabled() {
return this.newTodo.length < 4;
}

get isRemoveDoneTodosDisabled() {
return this.todos.items.filter(item => item.done).length === 0;
}

onNewTodoChange(newTodo: string) {
this.newTodo = newTodo;
}
Expand All @@ -49,4 +68,16 @@ export class TodosComponent implements OnInit, OnDestroy {
this.newTodo = '';
}

onToggleTodo(todo: Todo) {
this.store.dispatch(toggleTodo(todo.id));
}

onRemoveDoneTodos() {
this.store.dispatch(removeDoneTodos());
}

onFilterTodos(filter: TodoFilter) {
this.store.dispatch(filterTodos(filter));
}

}
28 changes: 28 additions & 0 deletions src/app/examples/todos/todos.effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';

import { LocalStorageService } from '../../core';

import {
TODOS_KEY,
TODOS_PERSIST,
} from './todos.reducer';

@Injectable()
export class TodosEffects {

constructor(
private actions$: Actions,
private localStorageService: LocalStorageService
) {}

@Effect({ dispatch: false }) persistTodos(): Observable<Action> {
return this.actions$
.ofType(TODOS_PERSIST)
.do(action => this.localStorageService
.setItem(TODOS_KEY, action.payload));
}

}
15 changes: 12 additions & 3 deletions src/app/examples/todos/todos.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { v4 as uuid } from 'node-uuid';

export const initialState = {
items: [
{ id: uuid(), name: 'Check the other example', done: false }
{ id: uuid(), name: 'Open Todo list example', done: true },
{ id: uuid(), name: 'Check the other examples', done: false },
{ id: uuid(), name: 'Use Angular ngRx Material Starter in your project', done: false }
],
filter: 'ALL'
};
Expand All @@ -15,10 +17,12 @@ export const TODOS_ADD = 'TODOS_ADD';
export const TODOS_TOGGLE = 'TODOS_TOGGLE';
export const TODOS_REMOVE_DONE = 'TODOS_REMOVE_DONE';
export const TODOS_FILTER = 'TODOS_FILTER';
export const TODOS_PERSIST = 'TODOS_PERSIST';

export const addTodo = (name: string) => ({ type: TODOS_ADD, payload: name });
export const toggleTodo = (id: string) => ({ type: TODOS_TOGGLE, payload: id });
export const removeDoneTodos = () => ({ type: TODOS_REMOVE_DONE });
export const persistTodos = (todos) => ({ type: TODOS_PERSIST, payload: todos });
export const filterTodos = (filter: TodoFilter) =>
({ type: TODOS_FILTER, payload: filter });

Expand All @@ -31,7 +35,7 @@ export function todosReducer(state = initialState, action: Action) {
});

case TODOS_TOGGLE:
state.items.some((item: any) => {
state.items.some((item: Todo) => {
if (item.id === action.payload) {
item.done = !item.done;
return true;
Expand All @@ -43,7 +47,7 @@ export function todosReducer(state = initialState, action: Action) {

case TODOS_REMOVE_DONE:
return Object.assign({}, state,
{ items: state.items.filter((item: any) => !item.done) });
{ items: state.items.filter((item: Todo) => !item.done) });

case TODOS_FILTER:
return Object.assign({}, state, { filter: action.payload });
Expand All @@ -53,3 +57,8 @@ export function todosReducer(state = initialState, action: Action) {
}
}

export interface Todo {
id: string;
name: string;
done: boolean;
}
6 changes: 2 additions & 4 deletions src/app/settings/settings.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { LocalStorageService } from '../core';
import {
SETTINGS_KEY,
SETTINGS_CHANGE_THEME,
useThemeAction
} from './settings.reducer';

@Injectable()
Expand All @@ -19,12 +18,11 @@ export class SettingsEffects {
private localStorageService: LocalStorageService
) {}

@Effect() persistThemeSettings(): Observable<Action> {
@Effect({ dispatch: false }) persistThemeSettings(): Observable<Action> {
return this.actions$
.ofType(SETTINGS_CHANGE_THEME)
.do(action => this.localStorageService
.setItem(SETTINGS_KEY, { theme: action.payload }))
.map(action => useThemeAction(action.payload));
.setItem(SETTINGS_KEY, { theme: action.payload }));
}

}
5 changes: 4 additions & 1 deletion src/app/settings/settings.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { EffectsModule } from '@ngrx/effects';

import { SharedModule } from '../shared';

import { SettingsComponent } from './settings/settings.component';
import { SettingsEffects } from './settings.effects';

@NgModule({
imports: [
CommonModule,
SharedModule
SharedModule,
EffectsModule.run(SettingsEffects)
],
declarations: [SettingsComponent]
})
Expand Down
6 changes: 1 addition & 5 deletions src/app/settings/settings.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ export const initialState = {

export const SETTINGS_KEY = 'SETTINGS';
export const SETTINGS_CHANGE_THEME = 'SETTINGS_CHANGE_THEME';
export const SETTINGS_USE_THEME = 'SETTINGS_USE_THEME';

export const changeThemeAction = (theme: string) =>
({ type: SETTINGS_CHANGE_THEME, payload: theme });

export const useThemeAction = (theme: string) =>
({ type: SETTINGS_USE_THEME, payload: theme });

export function settingsReducer(state = initialState, action: Action) {
switch (action.type) {
case SETTINGS_USE_THEME:
case SETTINGS_CHANGE_THEME:
return { theme: action.payload };

default:
Expand Down
2 changes: 1 addition & 1 deletion src/app/shared/big-input/big-input-action.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<button md-raised-button [color]="color" [disabled]="disabled">
<button md-raised-button [color]="color" [disabled]="disabled" (click)="onClick()">
<md-icon *ngIf="icon">{{icon}}</md-icon>
<span *ngIf="label">{{label}}</span>
</button>
Loading

0 comments on commit 2ba208c

Please sign in to comment.