Skip to content

Instantly share code, notes, and snippets.

@leehsueh
Created April 3, 2015 16:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leehsueh/5e68b416549669dfbcbf to your computer and use it in GitHub Desktop.
Save leehsueh/5e68b416549669dfbcbf to your computer and use it in GitHub Desktop.
Basic Flux Todo with Vanilla Angular
angular.module('fluxTodo')
.directive('todoApp', function() {
return {
restrict: 'EA',
template: '<h1>Flux To Do List <button ng-click="loadSeedTodos()">Load Sample</button></h1>' +
'<summary></summary>' +
'<new-todo></new-todo>' +
'<task-list></task-list>'
}
})
.directive('summary', function() {
return {
restrict: 'E',
template: '{{doneCount}} completed, {{undoneCount}} items left',
scope: {
change: '='
},
controller: function($scope, TodoStore) {
$scope.undoneCount = 0;
$scope.doneCount = 0;
var onChange = function() {
$scope.undoneCount = TodoStore.getUndoneCount();
$scope.doneCount = TodoStore.getDoneCount();
};
TodoStore.addChangeListener(onChange, 'summary');
}
};
})
.directive('taskList', function() {
return {
restrict: 'E',
controller: function($scope, TodoStore) {
var onChange = function() {
// get the updated list of tasks from the store
var tasks = TodoStore.getAll();
console.log(tasks);
var list = [];
for (var key in tasks) {
list.push(tasks[key]);
}
console.log('Handling Change')
console.log(list)
$scope.tasks = list;
};
// register change listener
TodoStore.addChangeListener(onChange, 'taskList');
},
template: '<ul>' +
'<li ng-repeat="t in tasks"><todo-item todo="t"></li></ul>'
};
})
.directive('todoItem', function() {
return {
restrict: 'EA',
scope: {
todo: '=',
},
controller: function($scope, TodoActionCreator) {
$scope.toggleTodo = function(id) {
TodoActionCreator.toggleTodo(id);
};
},
template: '<input type="checkbox" ng-checked="todo.completed" ng-click="toggleTodo(todo.id)"> {{todo.text}}'
};
})
.directive('newTodo', function() {
return {
restrict: 'EA',
template: '<input type="text" ng-model="newTodo"> <button ng-click="addTodo(newTodo); newTodo = null">Add</button>',
controller: function($scope, TodoActionCreator) {
$scope.addTodo = function(text) {
console.log(text);
TodoActionCreator.createTodo(text);
};
}
};
})
// naive Dispatcher implementation
// based on https://facebook.github.io/flux/docs/todo-list.html#content
angular.module('fluxTodo').factory('dispatcher', function() {
var _callbacks = [];
var _promises = [];
return {
register: function(callback) {
console.log('Registering');
_callbacks.push(callback);
return _callbacks.length -1 ;
},
dispatch: function(payload) {
console.log('Dispatching: ' + payload.action.actionType);
var resolves = [];
var rejects = [];
_promises = _callbacks.map(function(_, i) {
return new Promise(function(resolve, reject) {
resolves[i] = resolve;
rejects[i] = reject;
});
});
// dispatch to callbacks and reoslve/reject promises
console.log(_callbacks.length);
_callbacks.forEach(function(callback, i) {
Promise.resolve(callback(payload)).then(function() {
resolves[i](payload);
}, function() {
rejects[i](new Error('Dispatcher callback unsuccessful'));
});
});
_promises = [];
},
handleViewAction: function(action) {
this.dispatch({
source: 'VIEW_ACTION',
action: action
});
}
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.2.28" data-semver="1.2.28" src="https://code.angularjs.org/1.2.28/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script src="dispatcher.js"></script>
<script src="TodoStore.js"></script>
<script src="TodoActionCreator.js"></script>
<script src="directives.js"></script>
<script src="TodoCtrl.js"></script>
</head>
<body ng-app="fluxTodo">
<todo-app ng-controller="TodoCtrl"></todo-app>
</body>
</html>
angular.module('fluxTodo').factory('TodoActionCreator', function(dispatcher) {
return {
createTodo: function(text) {
dispatcher.handleViewAction({
actionType: 'CREATE',
text: text
});
},
toggleTodo: function(id) {
dispatcher.handleViewAction({
actionType: 'TOGGLE',
id: id
});
},
loadSeedTodos: function() {
dispatcher.handleViewAction({
actionType: 'SEED',
seedTodos: {
1423265707942: {
id: 1423265707942,
text: 'Read about Flux architecture',
completed: false
},
1423265707980: {
id: 1423265707980,
text: 'Study examples',
completed: false
},
1423265708000: {
id: 1423265708000,
text: 'Build something',
completed: false
},
}
});
}
};
})
angular.module('fluxTodo')
.controller('TodoCtrl', function($scope, TodoStore, TodoActionCreator) {
// $scope.todoStoreChange = TodoStore.emitChange;
// Seed
$scope.loadSeedTodos = function() {
TodoActionCreator.loadSeedTodos();
};
})
angular.module('fluxTodo').factory('TodoStore', function(dispatcher) {
// Event emitting and listening boilerplate
var _changeCallbacks = [];
var _todos = {};
function create(text) {
console.log('Creating todo: ' + text);
var id = Date.now();
_todos[id] = {
id: id,
complete: false,
text: text
};
}
function destroy(id) {
delete _todos[id];
}
var TodoStore = {
getAll: function() { return _todos; },
getUndoneCount: function() {
var count = 0;
for (var key in _todos) {
if (!_todos[key].completed) {
count++;
}
}
return count;
},
getDoneCount: function() {
return Object.keys(_todos).length - this.getUndoneCount();
},
emitChange: function(type) {
console.log('Broadcasting change from ' + type);
_changeCallbacks.forEach(function(callback) {
callback();
})
},
addChangeListener: function(callback, source) {
console.log('Adding change listener from ' + source);
_changeCallbacks.push(callback);
},
dispatcherIndex: dispatcher.register(function(payload) {
var action = payload.action;
var text;
console.log('Handling event: ' + action.actionType);
switch(action.actionType) {
case 'SEED':
_todos = action.seedTodos;
TodoStore.emitChange(action.actionType);
break;
case 'CREATE':
text = action.text.trim();
if (text !== '') {
create(text);
TodoStore.emitChange(action.actionType);
}
break;
case 'TOGGLE':
_todos[action.id].completed = !_todos[action.id].completed;
TodoStore.emitChange(action.actionType);
break;
case 'DESTROY':
destroy(action.id);
TodoStore.emitChange();
break;
}
return true;
})
};
return TodoStore;
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment