serving the solutions day and night

Pages

Monday, July 25, 2016

Backbone.js

http://backbonejs.org/

Models, Views & Collections












var Books = Backbone.Collection.extend({
  url: '/books'
});

Methods:
GET  /books/ .... collection.fetch();
POST /books/ .... collection.create();
GET  /books/1 ... model.fetch();
PUT  /books/1 ... model.save();
DEL  /books/1 ... model.destroy();



Example
{
  "page": 1,
  "limit": 10,
  "total": 2,
  "books": [
    {"id": 1, "title": "Pride and Prejudice"},
    {"id": 4, "title": "The Great Gatsby"}
  ]
}

var Books = Backbone.Collection.extend({
  url: '/books',
  parse: function(data) {
    return data.books;
  }
});


Routing with URLs
     








The Router detects changes to the URL. Pressing the "Back" button, and it will tell your application exactly where you are now.

Simple backbone appDeveloping Backbone.js Applications

Model.set()

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  }
});

// Setting the value of attributes via instantiation
var myTodo = new Todo({
  title: "Set through instantiation."
});

// Set single attribute value at a time through Model.set():
myTodo.set("title", "Title attribute set through Model.set().");

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

What is el? - The central property of a view is el.

What is el and how is it defined?

el is basically a reference to a DOM element and all views must have one. Views can use el to compose their element’s content and then insert it into the DOM all at once, which makes for faster rendering because the browser performs the minimum required number of reflows and repaints.

If you want to create a new element for your view, set any combination of the following properties on the view: tagName, id, and className.

Example creates a ul element with id and class attributes:

var TodosView = Backbone.View.extend({
  tagName: 'ul', // required, but defaults to 'div' if not set
  className: 'container', //optional
  id: 'todos' // optional
});

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

eland() - The view.$el property is equivalent to $(view.el) and view.$(selector) is equivalent to $(view.el).find(selector).

Collections - Collections are sets of Models and are created by extending Backbone.Collection.

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

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

var a = new Todo({ title: 'Jamaica.'}),
    b = new Todo({ title: 'China.'}),
    c = new Todo({ title: 'Disneyland.'});

var todos = new TodosCollection([a,b]); // todos.length = > 2

todos.add(c); // todos.length = > 3

todos.remove([a,b]); // todos.length = > 1

todos.remove(c); // todos.length = > 0

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

// pass array of models on collection instantiation
var todos = new TodosCollection([myTodo]);

var todo2 = todos.get(2);

// Models, as objects, are passed by reference
console.log(todo2 === myTodo); // true

var todo3 = todos.get(3); //undefined

Underscore.js utility functions
forEach: iterate over collections
todos.forEach(function(model){
  console.log(model.get('title'));
});

sortBy(): sort a collection on a specific attribute
var sortedByAlphabet = todos.sortBy(function (todo) {
    return todo.get("title").toLowerCase();
});

map(): iterate through a collection, mapping each value through a transformation function
var count = 1;
console.log(todos.map(function(model){
  return count++ + ". " + model.get('title');
}));

pluck(): extract a specific attribute
var names = collection.pluck('title'); // ['Belgium', 'China', 'Austria']

filter(): filter a collection
var Todos = Backbone.Collection.extend({
  model: Todo,
  filterById: function(ids){
    return this.filter(
      function(c) {
        return _.contains(ids, c.id);
      })
  }
});

groupBy(): group a collection into groups of like items
// create groups of completed and incomplete models
var byCompleted = todos.groupBy('completed');
var completed = new Backbone.Collection(byCompleted[true]);
console.log(completed.pluck('title')); //["Austria."]

pick(): extract a set of attributes from a model
var todo = new Todo({title: 'Austria.'}); // todo.pick('title') => {title: "Austria"}

omit(): extract all attributes from a model except those listed
console.log(todo.omit('title')) // {completed: false}

keys() and values(): get lists of attribute names and values
todo.keys() => ["title", "completed"]
todo.values() =>  ["Austria.", false]

pairs(): get list of attributes as [key, value] pairs
var pairs = todo.pairs();
pairs[0] => ["title", "Austria."]
pairs[1] = > ["completed", false]

invert(): create object in which the values are keys and the attributes are values
todo.invert()) = > {'go to Austria.': 'title', 'false': 'completed'}

Chainable API

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

var filteredNames = collection.chain() // start chain, returns wrapper around collection's models
  .filter(function(item) { return item.get('age') > 10; }) // returns wrapped array excluding Tim
  .map(function(item) { return item.get('name'); }) // returns wrapped array containing remaining names
  .value(); // terminates the chain and returns the resulting array

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

RESTful Persistence
Saving models to the server
var TodosCollection = Backbone.Collection.extend({
  model: Todo,
  url: '/todos'
});

var todos = new TodosCollection();
todos.fetch();

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

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

A model’s validate() method is called automatically by save() and will trigger an invalid event on the model if validation fails.

Deleting models from the server
var todo2 = todos.get(2);
todo2.destroy(); // sends HTTP DELETE to /todos/2 and removes from collection

//Calling destroy on a Model will return false if the model isNew:
var todo = new Backbone.Model();
console.log(todo.destroy()); // false

Routers
http://example.com/#about
http://example.com/#search/seasonal-horns/page2

var TodoRouter = Backbone.Router.extend({
    /* define the route and function maps for this router */
    routes: {
        "about" : "showAbout",
        /* Sample usage: http://example.com/#about */

        "todo/:id" : "getTodo",
        /* This is an example of using a ":param" variable which allows us to match
        any of the components between two URL slashes */
        /* Sample usage: http://example.com/#todo/5 */

        "search/:query" : "searchTodos",
        /* We can also define multiple routes that are bound to the same map function,
        in this case searchTodos(). Note below how we're optionally passing in a
        reference to a page number if one is supplied */
        /* Sample usage: http://example.com/#search/job */

        "search/:query/p:page" : "searchTodos",
        /* As we can see, URLs may contain as many ":param"s as we wish */
        /* Sample usage: http://example.com/#search/job/p1 */

        "todos/:id/download/*documentPath" : "downloadDocument",
        /* This is an example of using a *splat. Splats are able to match any number of
        URL components and can be combined with ":param"s*/
        /* Sample usage: http://example.com/#todos/5/download/files/Meeting_schedule.doc */

        /* If you wish to use splats for anything beyond default routing, it's probably a good
        idea to leave them at the end of a URL otherwise you may need to apply regular
        expression parsing on your fragment */

        "*other"    : "defaultRoute", // http://example.com/# <anything>

        "optional(/:item)": "optionalItem",
        "named/optional/(y:z)": "namedOptionalItem"
        /* Router URLs also support optional parts via parentheses, without having
           to use a regex.  */
    },

    showAbout: function(){
    },

    getTodo: function(id){
    },

    searchTodos: function(query, page){
    },

    downloadDocument: function(id, path){
    },

    defaultRoute: function(other){
        console.log('Invalid. You attempted to reach:' + other);
    }
});

new TodoRouter();

Backbone.history - This will automatically handle routes that have been defined and trigger callbacks when they’ve been accessed.
var TodoRouter = Backbone.Router.extend({
  /* define the route and function maps for this router */
  routes: {
    "about" : "showAbout",
    "search/:query" : "searchTodos",
    "search/:query/p:page" : "searchTodos"
  },

  showAbout: function(){},

  searchTodos: function(query, page){
    var page_number = page || 1;
    console.log("Page number: " + page_number + " of the results for todos containing the word: " + query);
  }
});

var myTodoRouter = new TodoRouter();

Backbone.history.start();

// http://localhost/#search/job/p3   logs: Page number: 3 of the results for todos containing the word: job
// http://localhost/#search/job      logs: Page number: 1 of the results for todos containing the word: job
// etc.

Backbone’s Sync API
Backbone.emulateHTTP = false; // set to true if server cannot handle HTTP PUT or HTTP DELETE
Backbone.emulateJSON = false; // set to true if server cannot handle application/json requests

Backbone.sync
Backbone.sync = function(method, model, options) {
};
var methodMap = {
  'create': 'POST',
  'update': 'PUT',
  'patch':  'PATCH',
  'delete': 'DELETE',
  'read':   'GET'
};

The sync method is called with three parameters:
method: One of create, update, patch, delete, or read
model: The Backbone model object
options: May include success and error methods
Implementing a new sync method can use the following pattern:

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

  function success(result) {
    // Handle successful results from MyAPI
    if (options.success) {
      options.success(result);
    }
  }

  function error(result) {
    // Handle error results from MyAPI
    if (options.error) {
      options.error(result);
    }
  }

  options || (options = {});

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

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

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

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

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

Nutritionix Health Tracker Project
Develop a single page app that tracks the user's calorie intake, and optionally, other health-related metrics. Typing food names into the search field will display a list of matching foods as provided by the health API. Users will be able to select an item from the list, and the item will be added to the list of foods the user is tracking. The total calorie count will also update to reflect the new daily total.

https://developer.nutritionix.com/docs/v1_1

Project Code

No comments: