Skip to content

Instantly share code, notes, and snippets.

@daffl
Last active May 17, 2016 20:39
Show Gist options
  • Select an option

  • Save daffl/6665992 to your computer and use it in GitHub Desktop.

Select an option

Save daffl/6665992 to your computer and use it in GitHub Desktop.
Feathers real-time todos
<!DOCTYPE html>
<html>
<head>
<title>Feathers real-time Todos</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<style type="text/css">
.done {
text-decoration: line-through;
}
</style>
<div class="container" id="todos">
<h1>Feathers real-time Todos</h1>
<ul class="todos list-unstyled"></ul>
<form role="form" class="create-todo">
<div class="form-group">
<input type="text" class="form-control" name="description" placeholder="Add a new Todo">
</div>
<button type="submit" class="btn btn-info col-md-12">Add Todo</button>
</form>
</div>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
var el = $('#todos');
var app = {
addTodo: function(todo) {
var html = '<li class="page-header checkbox" data-id="' + todo.id + '">' +
'<label><input type="checkbox" name="done">' +
todo.text +
'</label><a href="javascript://" class="pull-right delete">' +
'<span class="glyphicon glyphicon-remove"></span>' +
'</a></li>';
el.find('.todos').append(html);
app.updateTodo(todo);
},
removeTodo: function(todo) {
el.find('[data-id="' + todo.id + '"]').remove();
},
updateTodo: function(todo) {
var element = el.find('[data-id="' + todo.id + '"]');
var checkbox = element.find('[name="done"]').removeAttr('disabled');
if(todo.complete) {
element.addClass('done');
checkbox.prop('checked', true);
} else {
element.removeClass('done');
checkbox.prop('checked', false);
}
},
errorHandler: function(error) {
if(error) {
alert(error.message);
}
}
};
el.on('submit', 'form', function (ev) {
var field = $(this).find('[name="description"]');
socket.emit('todos::create', {
text: field.val()
}, {}, app.errorHandler);
field.val('');
ev.preventDefault();
}).on('click', '.delete', function (ev) {
var id = $(this).parents('li').data('id');
socket.emit('todos::remove', id, {}, app.errorHandler);
ev.preventDefault();
}).on('click', '[name="done"]', function(ev) {
var id = $(this).parents('li').data('id');
$(this).attr('disabled', 'disabled');
socket.emit('todos::update', id, {
complete: $(this).is(':checked')
}, {}, app.errorHandler);
});
socket.on('todos updated', app.updateTodo);
socket.on('todos removed', app.removeTodo);
socket.on('todos created', app.addTodo);
socket.emit('todos::find', {}, function (error, todos) {
todos.forEach(app.addTodo);
});
</script>
</body>
</html>
// To get up and running with this example, download the Gist from
// https://gist.github.com/daffl/6665992/download
// Unpack and in the new folder run
// > npm install body-parser feathers
// > node server.js
// Then go to http://localhost:8080
var feathers = require('feathers');
var bodyParser = require('body-parser');
var TodoService = require('./todos');
feathers()
.configure(feathers.rest())
.configure(feathers.socketio())
.use(bodyParser())
.use(feathers.static(__dirname))
.use('/todos', new TodoService())
.listen(8080);
var tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};
// Escapes HTML so that evil people can't inject mean things into the page
function escapeHtml(str) {
return str.replace(/[&<>]/g, function (tag) {
return tagsToReplace[tag] || tag;
});
}
function TodoStore() {
this.todos = [];
this.lastId = 0;
}
// Returns a Todo by it's id
TodoStore.prototype.getById = function (id) {
var currentTodo;
for (var i = 0; i < this.todos.length; i++) {
currentTodo = this.todos[i];
if (currentTodo.id == id) {
return currentTodo;
}
}
return null;
}
TodoStore.prototype.find = function (params, callback) {
callback(null, this.todos);
}
TodoStore.prototype.get = function (id, params, callback) {
var todo = this.getById(id);
if (todo === null) {
return callback(new Error('Todo not found'));
}
callback(null, todo);
}
TodoStore.prototype.create = function (data, params, callback) {
// Create our actual Todo object so that we only get what we really want
var newTodo = {
id: this.lastId++,
text: escapeHtml(data.text),
complete: !!data.complete
};
this.todos.push(newTodo);
callback(null, newTodo);
}
TodoStore.prototype.update = function (id, data, params, callback) {
var todo = this.getById(id);
if (todo === null) {
return callback(new Error('Todo does not exist'));
}
// We only want to update the `done` property
// !! is used for sanitization turning everything into a real booelan
todo.complete = !!data.complete;
callback(null, todo);
}
TodoStore.prototype.remove = function (id, params, callback) {
var todo = this.getById(id);
if (todo === null) {
return callback(new Error('Can not delete Todo'));
}
// Just splice our todo out of the array
this.todos.splice(this.todos.indexOf(todo), 1);
callback(null, todo);
}
module.exports = TodoStore;
@hotdogee
Copy link

The text of a todo is lost after being marked as done. Either include the original text in the update, or use patch and patched instead of update and updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment