Skip to content

Instantly share code, notes, and snippets.

@youknowriad
Created November 16, 2015 10:37
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save youknowriad/8ae9e15b157da3199c24 to your computer and use it in GitHub Desktop.
Angular.js Simple Todo, Store, Redux
angular.module('todoApp', []);
<!doctype html>
<html ng-app="todoApp">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/4.3.0/EventEmitter.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.0.4/redux.min.js"></script>
<script src="application.js"></script>
<script src="store.js"></script>
<script src="service.js"></script>
<script src="todo-actions.js"></script>
<script src="todos.js"></script>
<script src="todo-remaining.js"></script>
<script src="todo-create.js"></script>
</head>
<body>
<h2>Todo</h2>
<todo-remaining></todo-remaining>
<todos></todos>
<todo-create></todo-create>
</body>
</html>
function todoRepository(todoStore, $q) {
this.$q = $q;
this.store = todoStore;
this.todos = [
{id: 1, text:'learn angular', done:true},
{id: 2, text:'build an angular app', done:false}
];
this.nextId = 3;
}
todoRepository.prototype.findAll = function() {
return this.$q.when(angular.copy(this.todos));
}
todoRepository.prototype.update = function(todo) {
var index = this.todos.findIndex(function(elt) { return elt.id === todo.id });
if (index !== -1) {
this.todos[index] = todo;
}
return this.$q.when(angular.copy(todo));
}
todoRepository.prototype.create = function(todo) {
var newTodo = angular.copy(todo);
newTodo.id = this.nextId;
this.nextId++;
this.todos.push(newTodo);
return this.$q.when(newTodo);
}
todoRepository.prototype.archive = function() {
this.todos = this.todos.filter(function(todo) {
return !todo.done;
});
return this.$q.when(angular.copy(this.todos));
}
angular.module('todoApp').service('todoRepository', todoRepository);
function todoReducer(state, action) {
switch (action.type) {
case 'add-todo':
var newState = state.slice();
newState.push(angular.copy(action.todo));
return newState;
case 'update-todo':
return state.map(function(todo) {
if (todo.id === action.todo.id) {
return angular.copy(action.todo);
} else {
return todo;
}
});
case 'load-todos':
return action.todos.slice();
default:
return state;
}
}
function promiseMiddleware(store) {
return function(next) {
return function(action) {
return action && typeof action.then === 'function'
? action.then(result => store.dispatch(result))
: next(action);
}
}
}
angular.module('todoApp').factory('todoStore', function() {
var createStoreWithMiddleware = Redux.applyMiddleware(promiseMiddleware)(Redux.createStore);
return createStoreWithMiddleware(todoReducer, []);
});
function TodoActions(todoRepository) {
this.repository = todoRepository;
}
TodoActions.prototype.addTodo = function(todo) {
return this.repository.create(todo).then(function(createdTodo) {
return {
type: 'add-todo',
todo: createdTodo
};
});
}
TodoActions.prototype.updateTodo = function(todo) {
return this.repository.update(todo).then(function(updatedTodo) {
return {
type: 'update-todo',
todo: updatedTodo
};
});
}
TodoActions.prototype.loadTodos = function() {
return this.repository.findAll().then(function(todos) {
return {
type: 'load-todos',
todos: todos
};
});
}
TodoActions.prototype.archiveTodos = function() {
return this.repository.archive().then(function(todos) {
return {
type: 'load-todos',
todos: todos
};
});
}
angular.module('todoApp').service('todoActions', TodoActions);
angular.module('todoApp').directive('todoCreate', function() {
return {
restrict: 'E',
scope: true,
controller: function($scope, todoStore, todoActions) {
$scope.newTodo = {};
$scope.addTodo = function() {
todoStore.dispatch(todoActions.addTodo($scope.newTodo)).then(function() {
$scope.newTodo = {};
});
};
},
template: `
<form ng-submit="addTodo()">
<input type="text" ng-model="newTodo.text" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
`
}
});
angular.module('todoApp').directive('todoRemaining', function() {
return {
restrict: 'E',
scope: true,
controller: function($scope, todoStore, todoActions) {
todoStore.dispatch(todoActions.loadTodos());
$scope.todos = todoStore.getState();
todoStore.subscribe(function(todos) {
$scope.todos = todoStore.getState();
});
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.archive = function() {
todoStore.dispatch(todoActions.archiveTodos());
};
},
template: `
<span>{{remaining()}} of {{todos.length}} remaining</span>
[ <a href="" ng-click="archive()">archive</a> ]
`
}
});
angular.module('todoApp').directive('todos', function() {
return {
restrict: 'E',
scope: true,
controller: function($scope, todoStore, todoActions) {
todoStore.dispatch(todoActions.loadTodos());
$scope.todos = todoStore.getState();
todoStore.subscribe(function(todos) {
$scope.todos = todoStore.getState();
});
$scope.update = function(todo) {
todoActions.updateTodo(todo);
}
},
template: `
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done" ng-change="update(todo)">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
`
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment