¿Cómo de sexy puede hacer Backbone mi código?

45
Introducción a Backbone.js Sergio Espín ¿Cómo de sexy puede hacer Backbone mi código? Sergio Espín @bytespin

description

Introducción práctica a Backbone.js. Presentación perteneciente al evento Betabeers Murcia del 9 de mayo de 2014. Impartida por Sergio Espín @bytespin

Transcript of ¿Cómo de sexy puede hacer Backbone mi código?

Page 1: ¿Cómo de sexy puede hacer Backbone mi código?

Introducción a Backbone.js

Sergio Espín¿Cómo de sexy puede hacer Backbone mi código?

Sergio Espín@bytespin

Page 2: ¿Cómo de sexy puede hacer Backbone mi código?

¿Qué es Backbone?

● Librería JavaScript muy ligera para desarrollo de aplicaciones web

● Ofrece:

● Estructura de datos: Modelos y Colecciones

● Interfaz: Vistas y Rutas

● MVC

● RESTful

Page 3: ¿Cómo de sexy puede hacer Backbone mi código?

¿Qué es Backbone?

● Librería JavaScript muy ligera para desarrollo de aplicaciones web

● Ofrece:

● Estructura de datos: Modelos y Colecciones

● Interfaz: Vistas y Rutas

● MV*

● RESTful

Page 4: ¿Cómo de sexy puede hacer Backbone mi código?

Dependencias

● Jquery

● Underscore

● Backbone

Page 5: ¿Cómo de sexy puede hacer Backbone mi código?

Dependencias

Modelo

Page 6: ¿Cómo de sexy puede hacer Backbone mi código?

Modelo

var Todo = Backbone.Model.extend({});

var todo1 = new Todo();

console.log(JSON.stringify(todo1));// Logs: {}

var todo2 = new Todo({ title: 'Check the attributes of both model instances in the console.', completed: true});

console.log(JSON.stringify(todo2));// Logs: {"title":"Check the attributes of both model instances in the console.","completed":true}

Page 7: ¿Cómo de sexy puede hacer Backbone mi código?

Inicialización

var Todo = Backbone.Model.extend({ initialize: function(){ console.log('This model has been initialized.'); }});

var myTodo = new Todo();// Logs: This model has been initialized.

Modelo

Page 8: ¿Cómo de sexy puede hacer Backbone mi código?

Valores por defecto

var Todo = Backbone.Model.extend({ defaults: { title: '', completed: false }});

var todo2 = new Todo({ title: 'Check attributes of the logged models in the console.'});

console.log(JSON.stringify(todo2));// Logs: {"title":"Check attributes of the logged models in the console.","completed":false}

Modelo

Page 9: ¿Cómo de sexy puede hacer Backbone mi código?

Get, set y toJSON

● Get

console.log(todo1.get('completed')); // false

● Set

myTodo.set("title", "Title attribute set through Model.set().");

myTodo.set({ title: "Both attributes set through Model.set().", completed: true});

● ToJSON

var todo1Attributes = todo1.toJSON(); console.log(todo1Attributes);// Logs: {"title":"","completed":false}

Modelo

Page 10: ¿Cómo de sexy puede hacer Backbone mi código?

Cambios en el modelo

var Todo = Backbone.Model.extend({ defaults: { title: '', completed: false },

initialize: function(){ console.log('This model has been initialized.');

this.on('change', function(){ console.log('Values for this model have changed.'); });

this.on('change:title', function(){ console.log('Title value for this model has changed.'); }); }});

Modelo

Page 11: ¿Cómo de sexy puede hacer Backbone mi código?

Validación

validate: function(attributes){ if(attributes.title === undefined){ return "Remember to set a title for your todo."; } },

initialize: function(){ console.log('This model has been initialized.');

this.on("invalid", function(model, error){ console.log(error); }); }

var myTodo = new Todo();myTodo.set('completed', true, {validate: true}); // logs: Remember to set a title for your todo.console.log(myTodo.get('completed')); // logs: falseconsole.log(myTodo.validationError) //logs: Remember to set a title for your todo.

Modelo

Page 12: ¿Cómo de sexy puede hacer Backbone mi código?

Dependencias

Vista

Page 13: ¿Cómo de sexy puede hacer Backbone mi código?

Vista

var TodoView = Backbone.View.extend({

tagName: 'ul', className: 'container ui-corner', id: 'todos'

template: _.template( $('item-template').html() ),

render: function() { this.$el.html( this.template( this.model.toJSON() ) ); return this; }});

var todoView = new TodoView();

console.log(todosView.el); //logs <ul id="todos" class="container"></ul>

Page 14: ¿Cómo de sexy puede hacer Backbone mi código?

Eventos

var TodoView = Backbone.View.extend({ tagName: 'li',

events: { 'click .toggle': 'toggleCompleted', 'dblclick label': 'edit', 'keypress .edit': 'updateOnEnter', 'click .destroy': 'clear', 'blur .edit': 'close' }, edit:function(){

},

Vista

Page 15: ¿Cómo de sexy puede hacer Backbone mi código?

Eventos

var TodoView = Backbone.View.extend({

initialize: function() { this.model.bind('change', _.bind(this.render, this)); }

});

Vista

Page 16: ¿Cómo de sexy puede hacer Backbone mi código?

Dependencias

Colecciones

Page 17: ¿Cómo de sexy puede hacer Backbone mi código?

Colecciones

var Todo = Backbone.Model.extend({ defaults: { title: '', completed: false }});

var TodosCollection = Backbone.Collection.extend({ model: Todo});

var myTodo = new Todo({title:'Read the whole book', id: 2});

var todos = new TodosCollection([myTodo]);console.log("Collection size: " + todos.length); // Collection size: 1

Page 18: ¿Cómo de sexy puede hacer Backbone mi código?

Añadir, obtener y eliminar modelos

var a = new Todo({ title: 'Go to Jamaica.', id:1}), b = new Todo({ title: 'Go to China.', id:2}), c = new Todo({ title: 'Go to Disneyland.', id:3});

var todos = new TodosCollection([a,b]);console.log("Collection size: " + todos.length);// Logs: Collection size: 2

todos.add(c);console.log("Collection size: " + todos.length);// Logs: Collection size: 3

todos.remove([a,b]);console.log("Collection size: " + todos.length);// Logs: Collection size: 1

var todo2 = todos.get(2);// logs: { title: 'Go to China.', id:2}

Colecciones

Page 19: ¿Cómo de sexy puede hacer Backbone mi código?

Actualizando

var TodosCollection = new Backbone.Collection();

TodosCollection.add([ { id: 1, title: 'go to Jamaica.', completed: false }, { id: 2, title: 'go to China.', completed: false }, { id: 3, title: 'go to Disneyland.', completed: true }]);

TodosCollection.set([ { id: 1, title: 'go to Jamaica.', completed: true }, { id: 2, title: 'go to China.', completed: false }, { id: 4, title: 'go to Disney World.', completed: false }]);

Console.log(TodosCollection.toJSON());// Logs: [ { id: 1, title: 'go to Jamaica.', completed: true },// { id: 2, title: 'go to China.', completed: false },// { id: 4, title: 'go to Disney World.', completed: false } ]

Colecciones

Page 20: ¿Cómo de sexy puede hacer Backbone mi código?

Eventos

var TodosCollection = new Backbone.Collection();

TodosCollection.on("change:title", function(model) { console.log("Changed my mind! I should " + model.get('title'));});

TodosCollection.add([ { title: 'go to Jamaica.', completed: false, id: 3 },]);

var myTodo = TodosCollection.get(3);

myTodo.set('title', 'go fishing');// Logs: Changed my mind! I should go fishing

Colecciones

Page 21: ¿Cómo de sexy puede hacer Backbone mi código?

Reiniciando

TodosCollection.reset();

TodosCollection.reset([ { title: 'go to Cuba.', completed: false }]);

Colecciones

Page 22: ¿Cómo de sexy puede hacer Backbone mi código?

Funciones Underscore

● forEach● sortBy● map● max/min● pulck● filter● indexOf● any/some● isEmpty● groupBy● pick● omit● keys/values● pairs● Invert

● chain

Page 23: ¿Cómo de sexy puede hacer Backbone mi código?

Funciones Underscore

● forEach● sortBy● map● max/min● pulck● filter● indexOf● any/some● isEmpty● groupBy● pick● omit● keys/values● pairs● Invert

● chain

var sortedByAlphabet = todos.sortBy(function (todo) { return todo.get("title").toLowerCase();});

console.log("- Now sorted: ");

sortedByAlphabet.forEach(function(model){ console.log(model.get('title'));});

// Above logs:// - Now sorted:// go to Austria.// go to Belgium.// go to China.

Page 24: ¿Cómo de sexy puede hacer Backbone mi código?

Funciones Underscore

● forEach● sortBy● map● max/min● pulck● filter● indexOf● any/some● isEmpty● groupBy● pick● omit● keys/values● pairs● Invert

● chain

todos.max(function(model){ return model.id;}).id;

todos.min(function(model){ return model.id;}).id;

Page 25: ¿Cómo de sexy puede hacer Backbone mi código?

Funciones Underscore

● forEach● sortBy● map● max/min● pulck● filter● indexOf● any/some● isEmpty● groupBy● pick● omit● keys/values● pairs● Invert

● chain

var collection = new Backbone.Collection([ { name: 'Tim', age: 5 }, { name: 'Ida', age: 26 }, { name: 'Rob', age: 55 }]);

var filteredNames = collection.chain().filter(function(item) { return item.get('age') > 10; }).map(function(item) { return item.get('name'); }) .value();

console.log(filteredNames); // logs: ['Ida', 'Rob']

Page 26: ¿Cómo de sexy puede hacer Backbone mi código?

RESTful Persistence

var Todo = Backbone.Model.extend({ defaults: { title: '', completed: false }});

var TodosCollection = Backbone.Collection.extend({ model: Todo, url: '/todos'});

var todos = new TodosCollection();todos.fetch(); // sends HTTP GET to /todos

var todo2 = todos.get(2);todo2.set('title', 'go fishing');todo2.save(); // sends HTTP PUT to /todos/2todo2.destroy(); // sends HTTP DELETE to /todos/2

todos.create({title: 'Try out code samples'}); // sends HTTP POST to /todos

Page 27: ¿Cómo de sexy puede hacer Backbone mi código?

BackBone-sync

Backbone.sync = function(method, model, options) {

switch (method) { case 'POST': return MyAPI.create(model, success, error);

case 'PUT': return MyAPI.update(model, success, error);

case 'PATCH': return MyAPI.patch(model, success, error);

case 'DELETE': return MyAPI.destroy(model, success, error);

case 'GET': if (model.cid) { return MyAPI.find(model, success, error); } else { return MyAPI.findAll(model, success, error); } }}

Page 28: ¿Cómo de sexy puede hacer Backbone mi código?

Rutas

var TodoRouter = Backbone.Router.extend({ routes: { "todo/:id" : "getTodo", // http://example.com/#todo/5 "search/:query" : "searchTodos", // http://example.com/#search/job "search/:query/p:page" : "searchTodos", // http://example.com/#search/job/p1 "todos/:id/download/*documentPath" : "downloadDocument", //http://example.com/#todos/5/download/files/Meeting_schedule.doc

}, searchTodos: function(query, page){ }, downloadDocument: function(id, path){ }});

var myTodoRouter = new TodoRouter();

Page 29: ¿Cómo de sexy puede hacer Backbone mi código?

Ejemplo práctico

todomvc.com

Page 30: ¿Cómo de sexy puede hacer Backbone mi código?

Ejemplo práctico

Modelo

Template

Template

item-template

Todo

stats-template

Colección

TodoList

Vista

AppView

TodoView

Vista

Rutas

TodoRouter

Page 31: ¿Cómo de sexy puede hacer Backbone mi código?

HTML

<section id="todoapp"> <header id="header"> <h1>todos</h1> <input id="new-todo" placeholder="What needs to be done?" autofocus> </header> <section id="main"> <input id="toggle-all" type="checkbox"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"></ul> </section> <footer id="footer"></footer> </section> <div id="info"> <p>Double-click to edit a todo</p> <p>Written by <a href="https://github.com/addyosmani">Addy Osmani</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p> </div>

Ejemplo práctico

Page 32: ¿Cómo de sexy puede hacer Backbone mi código?

Templates

<!-- index.html -->

<script type="text/template" id="item-template"> <div class="view"> <input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>> <label><%= title %></label> <button class="destroy"></button> </div> <input class="edit" value="<%= title %>"> </script>

Ejemplo práctico

Page 33: ¿Cómo de sexy puede hacer Backbone mi código?

Templates

<!-- index.html -->

<script type="text/template" id="stats-template"> <span id="todo-count"><strong><%= remaining %></strong> <%= remaining === 1 ? 'item' : 'items' %> left</span> <ul id="filters"> <li> <a class="selected" href="#/">All</a> </li> <li> <a href="#/active">Active</a> </li> <li> <a href="#/completed">Completed</a> </li> </ul> <% if (completed) { %> <button id="clear-completed">Clear completed (<%= completed %>)</button> <% } %> </script>

Ejemplo práctico

Page 34: ¿Cómo de sexy puede hacer Backbone mi código?

Todo Model

var app = app || {};

app.Todo = Backbone.Model.extend({

defaults: { title: '', completed: false },

toggle: function() { this.save({ completed: !this.get('completed') }); }

});

Ejemplo práctico

Page 35: ¿Cómo de sexy puede hacer Backbone mi código?

Todo Collection

var TodoList = Backbone.Collection.extend({

model: app.Todo,

completed: function() { return this.filter(function( todo ) { return todo.get('completed'); }); },

remaining: function() { return this.without.apply( this, this.completed() ); },

comparator: function( todo ) { return todo.get('order'); } });

app.Todos = new TodoList();

Ejemplo práctico

Page 36: ¿Cómo de sexy puede hacer Backbone mi código?

Application View

app.AppView = Backbone.View.extend({ el: '#todoapp', statsTemplate: _.template( $('#stats-template').html() ),

initialize: function() { this.allCheckbox = this.$('#toggle-all')[0]; this.$input = this.$('#new-todo'); this.$footer = this.$('#footer'); this.$main = this.$('#main');

this.listenTo(app.Todos, 'add', this.addOne); this.listenTo(app.Todos, 'reset', this.addAll); this.listenTo(app.Todos, 'change:completed', this.filterOne); this.listenTo(app.Todos,'filter', this.filterAll); this.listenTo(app.Todos, 'all', this.render);

app.Todos.fetch(); } });

Ejemplo práctico

Page 37: ¿Cómo de sexy puede hacer Backbone mi código?

Application View

app.AppView = Backbone.View.extend({ events: { 'keypress #new-todo': 'createOnEnter', 'click #clear-completed': 'clearCompleted', 'click #toggle-all': 'toggleAllComplete' }, addOne: function( todo ) { var view = new app.TodoView({ model: todo }); $('#todo-list').append( view.render().el ); }, addAll: function() { this.$('#todo-list').html(''); app.Todos.each(this.addOne, this); }

render: function() {

this.$footer.html(this.statsTemplate({ completed: app.Todos.completed().length, remaining: app.Todos.remaining().length })); }, });

Ejemplo práctico

Page 38: ¿Cómo de sexy puede hacer Backbone mi código?

Application View

app.AppView = Backbone.View.extend({

newAttributes: function() { return { title: this.$input.val().trim(), order: app.Todos.nextOrder(), completed: false }; },

createOnEnter: function( event ) { if ( event.which !== ENTER_KEY || !this.$input.val().trim() ) { return; }

app.Todos.create( this.newAttributes() ); this.$input.val(''); },

});

Ejemplo práctico

Page 39: ¿Cómo de sexy puede hacer Backbone mi código?

Application View

app.AppView = Backbone.View.extend({

clearCompleted: function() { _.invoke(app.Todos.completed(), 'destroy'); return false; },

toggleAllComplete: function() { var completed = this.allCheckbox.checked;

app.Todos.each(function( todo ) { todo.save({ 'completed': completed }); }); }

});

Ejemplo práctico

Page 40: ¿Cómo de sexy puede hacer Backbone mi código?

Individual Todo View

app.TodoView = Backbone.View.extend({ tagName: 'li', template: _.template( $('#item-template').html() ),

events: { 'click .toggle': 'togglecompleted', 'dblclick label': 'edit', 'click .destroy': 'clear', 'keypress .edit': 'updateOnEnter', 'blur .edit': 'close' }, initialize: function() { this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'destroy', this.remove); this.listenTo(this.model, 'visible', this.toggleVisible); }, render: function() { this.$el.html( this.template( this.model.toJSON() ) ); return this; }, });

Ejemplo práctico

Page 41: ¿Cómo de sexy puede hacer Backbone mi código?

Individual Todo View

app.TodoView = Backbone.View.extend({ close: function() { var value = this.$('.edit).val().trim();

if ( value ) { this.model.save({ title: value }); } else { this.clear(); }

this.$el.removeClass('editing'); },

updateOnEnter: function( e ) { if ( e.which === ENTER_KEY ) { this.close(); } },

clear: function() { this.model.destroy(); } });

Ejemplo práctico

Page 42: ¿Cómo de sexy puede hacer Backbone mi código?

Todo routing

var Workspace = Backbone.Router.extend({ routes:{ '*filter': 'setFilter' },

setFilter: function( param ) { if (param) { param = param.trim(); } app.TodoFilter = param || '';

app.Todos.trigger('filter'); } });

app.TodoRouter = new Workspace(); Backbone.history.start();

Ejemplo práctico

Page 43: ¿Cómo de sexy puede hacer Backbone mi código?

Startup

var app = app || {};

var ENTER_KEY = 13;

$(function() {

new app.AppView().render();

});

Ejemplo práctico

Page 44: ¿Cómo de sexy puede hacer Backbone mi código?

¿Qué nos queda de backbone?

● Frameworks

● MarionetteJS● Thorax(Handlebars)

● RequireJS – Organización modular

● Backbone.Paginator

● Unit Testing

Page 45: ¿Cómo de sexy puede hacer Backbone mi código?

¿ Preguntas ?