Skip to content

Instantly share code, notes, and snippets.

@bruschill
Created February 19, 2014 01:15
Show Gist options
  • Save bruschill/9084274 to your computer and use it in GitHub Desktop.
Save bruschill/9084274 to your computer and use it in GitHub Desktop.
Angular todo app with list management
'use strict';
var rpgtd = angular.module('rpgtd', []);
<!DOCTYPE html>
<html lang="en" ng-app="rpgtd">
<head>
<title>RPGTD</title>
<link rel="stylesheet" href="/css/base.css"/>
<link rel="stylesheet" href="/css/grid.css"/>
<link rel="stylesheet" href="/css/lists.css"/>
<link rel="stylesheet" href="/css/tasks.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
</head>
<body ng-cloak>
<div class="pure-g-r">
<div class="grid">
<div class="col-1-5">
<div ng-controller="ListsController">
<header id="lists-header">
<h1>Lists</h1>
<item-input add-fn="addList()" model-attr="newListName" placeholder-text="List name..."></item-input>
<lists-detail></lists-detail>
</header>
</div>
</div>
<div class="col-4-5">
<div ng-controller="TasksController">
<header id="list-header" ng-show="!!listName">
<h1>{{ listName }}</h1>
<item-input add-fn="addTask()" model-attr="newTaskName" placeholder-text="Task name..."></item-input>
<tasks-detail></tasks-detail>
</header>
</div>
</div>
</div>
</div>
</body>
<script src="js/app.js"></script>
<script src="js/decorators/onRootScope.js"></script>
<script src="js/services/listStorage.js"></script>
<script src="js/services/taskStorage.js"></script>
<script src="js/directives/itemInput.js"></script>
<script src="js/controllers/listsController.js"></script>
<script src="js/directives/listsDetail.js"></script>
<script src="js/controllers/tasksController.js"></script>
<script src="js/directives/tasksDetail.js"></script>
</html>
<form id="item-form" ng-submit="addFn()">
<input id="new-item" placeholder="{{placeholderText}}" ng-model="modelAttr"></input>
</form>
'use strict;'
rpgtd.directive('itemInput', function() {
return {
restrict: 'E',
replace: true,
scope: {
addFn: '&',
modelAttr: '=',
placeholderText: '@'
},
templateUrl: 'views/itemInput.html'
};
});
'use strict';
rpgtd.controller('ListsController', ['$scope', '$rootScope', 'listStorage', 'filterFilter', function ListsController($scope, $rootScope, listStorage, filterFilter) {
var lists = $scope.lists = listStorage.get();
$scope.newListName = '';
$scope.addList = function() {
var newListName = $scope.newListName.trim();
if(!newListName.length) {
return;
}
lists.push({
name: newListName,
tasks: {}
});
listStorage.put(lists);
$scope.newListName = '';
};
$scope.selectList = function(list) {
localStorage.setItem('rpgtd-selected-list', list.name);
$rootScope.$emit('listSelected', list.name);
};
//TODO this removes the list, but also removes the tasks from every other list as well...
$scope.removeList = function(list) {
if(list.name === localStorage.getItem('rpgtd-selected-list')) {
localStorage.removeItem('rpgtd-selected-list');
$rootScope.$emit('listSelected', null);
}
lists.splice(lists.indexOf(list), 1);
listStorage.put(lists);
};
}]);
<section ng-show="lists.length">
<ul class="lists">
<li class="list-container cf" ng-repeat="list in lists | filter:statusFilter track by $index">
<div class="list-name" ng-click="selectList(list)">{{ list.name }}</div>
<button class="btn-destroy-list" ng-click="removeList(list)">Remove</button>
</li>
</ul>
</section>
'use strict';
rpgtd.directive('listsDetail', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'views/listsDetail.html',
controller: 'ListsController'
};
});
'use strict';
rpgtd.service('taskStorage', function() {
var STORAGE_ID = 'rpgtd-lists';
return {
getTasks: function(listName) {
var lists = angular.fromJson(localStorage.getItem(STORAGE_ID));
if(!listName || !lists) {
return [];
} else {
for(var i = 0; i < lists.length; i++) {
if(lists[i].name === listName){
var tasks = lists[i].tasks;
if(Object.keys(tasks).length === 0) {
return [];
} else {
return tasks;
}
}
}
}
},
putTasks: function(tasks, listName) {
var lists = angular.fromJson(localStorage.getItem(STORAGE_ID));
for(var i = 0; i < lists.length; i++) {
if(lists[i].name === listName){
lists[i].tasks = tasks;
localStorage.setItem(STORAGE_ID, angular.toJson(lists));
}
}
}
};
});
rpgtd.config(['$provide', function($provide){
$provide.decorator('$rootScope', ['$delegate', function($delegate){
$delegate.constructor.prototype.$onRootScope = function(name, listener){
var unsubscribe = $delegate.$on(name, listener);
this.$on('$destroy', unsubscribe);
};
return $delegate;
}]);
}]);
'use strict';
rpgtd.controller('TasksController', ['$scope', 'taskStorage', 'filterFilter', function TasksController($scope, taskStorage, filterFilter) {
var listName = $scope.listName = localStorage.getItem('rpgtd-selected-list') || null
var tasks = $scope.tasks = taskStorage.getTasks(listName);
$scope.newTaskName = '';
$scope.remainingTasksCount = filterFilter(tasks, {completed: false}).length;
$scope.$onRootScope('listSelected', function(event, selectedListName) {
listName = $scope.listName = selectedListName;
tasks = $scope.tasks = taskStorage.getTasks(selectedListName);
$scope.remainingTasksCount = filterFilter(tasks, {completed: false}).length;
});
$scope.addTask = function() {
var newTaskName = $scope.newTaskName.trim();
if(!newTaskName.length) {
return;
}
tasks.push({
name: newTaskName,
completed: false
});
taskStorage.putTasks(tasks, listName);
$scope.newTaskName = '';
$scope.remainingTasksCount++;
};
$scope.removeTask = function(task) {
$scope.remainingTasksCount -= task.completed ? 0 : 1;
tasks.splice(tasks.indexOf(task), 1);
taskStorage.putTasks(tasks, listName);
};
$scope.taskCompleted = function(task) {
$scope.remainingTasksCount += task.completed ? -1 : 1;
taskStorage.putTasks(tasks, listName);
};
}]);
<section ng-show="tasks.length">
<ul class="tasks">
<li class="task-container cf" ng-repeat="task in tasks | filter:statusFilter track by $index">
<div class="task-info">
<input type="checkbox" class="task-complete" ng-model="task.completed" ng-change="taskCompleted(task)"></input>
<div class="task-name">{{ task.name }}</div>
</div>
<button class="btn-task-destroy" ng-click="removeTask(task)">Remove</button>
</li>
</ul>
<footer id="footer" ng-show="tasks.length">
<span id="task-count">
<strong>{{ remainingTasksCount }} <span ng-pluralize count="remainingTasksCount" when="{one: 'task left', other: 'tasks left'}"></span></strong>
</span>
</footer>
</section>
'use strict';
rpgtd.directive('tasksDetail', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'views/tasksDetail.html',
controller: 'TasksController'
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment