Пользователь
0,0
рейтинг
5 мая 2011 в 19:15

Разработка → Написание сложных интерфейсов с Backbone.js

image

Backbone.js это каркас для создания RIA JavaScript приложений, его автором является Jeremy Ashkenas, создатель CoffeeScript, Backbone является частью компании Document Cloud ей же «принадлежит» Underscrore.js. Backbone — очень легкая библиотека, помогающая вам создавать интерфейсы. Она может работать с любыми библиотеками, к которым вы привыкли.
Backbone это набор классов, размером менее 4Кб, которые формируют структуру вашего кода и помогают создавать качественные MVC веб-приложения.
Backbone формирует структуру тяжелых JavaScript приложений, внесением моделей с key-value подобным хранилищем и своими событиями, коллекций с богатыми API, видов (ориг. views) с декларативной обработкой событий и соединяет все это в в одно приложение, поддерживающее RESTful JSON интерфейс.

Backbone не может работать без Underscore.js. Для поддержки REST API и работы с DOM элементами в Backbone.View настоятельно рекомендуется подключить json2.js и jQuery-подобную библиотеку: jQuery или Zepto

В статье будет рассмотрена структура Backbone.js, будет поэтапно создано простое Todo приложение.

Вот те рычаги, которые нам дает Backbone

Хранилище типа ключ-значение и пользовательские события


Когда содержание модели изменяется все объекты, которые были подписаны на изменения модели получают уведомления и могут предпринять дальнейшие действия. Например, виды (ориг. views) слушают изменения в модели и обновляют свое состояние вместо того, чтобы модель меняла состояние видов. Применяется паттерн loose coupling «слабая связь».

Богатый API перечисляемых функций


Backbone поставляется с набором очень полезных функций для обработки ваших данных. В отличии от других языков, в JavaScript массивы «недоделанные», что доставляет много хлопот если вы работаете с большим объемом данных.

Виды с декларативной обработкой событий


Те дни, когда вы писали спагетти-подобный код подходят к концу. Вы можете программно определить какой callback ассоциирован с каким объектом.

RESTful JSON интерфейс


Если вам необходимо пообщаться с сервером, вам приходится выполнить AJAX запрос в коде «вида» и получать то, что вам необходимо. Все это частично упрощается с использованием различных xhr-адаптеров, WebSockets и localStorage, но можно сделать проще. Такую сущность следует поделить на несколько мелких, Backbone нам в этом поможет:
Backbone дает нам возможность отделить данные от представления. Модель, которая работает с данными и синхронизируется с сервером, тогда когда вид слушает изменения модели и изменяет свое состояние (отрисовывает данные в HTML)
Сразу перечислю ответы на вопросы, которые могут сейчас возникнуть:

Заменяет ли оно jQuery?

Нет. Они очень отличаются в своих целях. Backbone работает с высокоуровневыми абстракциями, в то время как jQuery или подобные библиотеки работают с DOM, нормализует события и так далее.

У них различные области применения, если вы знаете одну библиотеку это не значит, что вам нужно знать другую. Но как JavaScript разработчик, вы должны знать, как эффективно работать с обеими.

Почему я должен использовать её?

Потому, что чаще всего код интерфейса представляет из себя грязный набор вложенных callbacks'ов, DOM манипуляций, HTML шаблоны или функции, генерирующие HTML для представления данных. Backbone предлагает отличный инструмент для управления этим хаосом.

Где я должен использовать её?

Backbone идеально подходит для создания тяжелых интерфейсов и приложений, управляемых данными. Например, интерфейс GMail, новый Twitter или другие связанные приложения. Backbone позволяет создавать сложные приложения проще.

С её помощью вы можете создавать и простые html страницы, насыщенные JavaScript, но по большему счету Backbone предназначен для создания веб-приложений.

Похож ли он на Cappuccino или Sproutcore?

И да и нет. Да потому, что как и в вышеупомянутых фрэймворках, основная цель — создание сложных интерфейсов для веб-приложений. Отличаются они тем, что Backbone очень легкий, ни одна вышеупомянутая библиотека не может сравниться с ним.
Backbone предельно легкий, менее 4кб.
На самом деле про 4kb это не правда: Backbone 4кб + Underscore.js 3кб + jQuery 31Кб = 38кб

Cappuccino заставляет вас писать код на Objective-J, тогда как виды Sproutcore должны объявляться в JavaScript коде. Но я не могу назвать ни один из этих подходом не верным, но с Backbone вы пишете обычный JavaScript и используете обычный HTML и CSS вам практически ничего не нужно менять.

Могу ли я использовать другие библиотеки вместе с Backbone?

Безусловно. Не только обращение к DOM, обертки AJAX, но также шаблоны и загрузчики скриптов. Backbone имеет очень-очень слабую связь (very, very loosely coupled) — это значит, что вы можете использовать практически все ваши инструменты в сочетании с Backbone.

Строение Backbone


Вначале Backbone состоял только из моделей, видов и коллекций — контроллеры не входили. Со временем контроллеры были добавлены. Сейчас Backbone состоит из 4 основных классов:
  • Модель (Model)
  • Коллекция (Collection)
  • Вид (View)
  • Контроллер (Controller)
Кратко пробежимся по основным классам, затем на основе этих знаний создадим простое приложение.

Модель

Моделями называются разные вещи в различных MVC фрэймворках. В Backbone моделью называется отдельная сущность, ближайший аналог — запись в базе. Но здесь нет жестких правил. С сайта фрэймворка:
Модели это сердце всех JavaScript приложений, содержащие интерактивные данные также как и большую часть логики, обрамляющую их: преобразования, валидация, вычисляемые свойства, разграничение прав доступа.
Модель — это способ чтения и записи свойств или атрибутов в наборе данных. Вот пример модели:
var Game = Backbone.Model.extend({});

Давайте немного усложним:
var Game = Backbone.Model.extend({
       initialize: function(){
           alert("Oh hey! ");
       },
       defaults: {
           name: 'Default title',
           releaseDate: 2011,
       }
   });

Метод initialize будет выполнен, когда создастся объект. В этом методе я не делаю ничего полезного. Также я определяю несколько переменных по умолчанию, на случай если часть данных не будет передана.

Давайте посмотрим как читать и записывать атрибуты. Но сперва, создадим объект:
// Создаем новую игру
var portal = new Game({ name: "Portal 2", releaseDate: 2011});

// дата релиза находится в releaseDate
var release = portal.get('releaseDate'); // 2011

// Меняем имя игры
portal.set({ name: "Portal 2 by Valve"});

Вы не можете работать с атрибутами напрямую (object.attribute) вы должны вызвать метод для изменения или получения данных (думаю с появлением Proxy ситуация изменится).
Сейчас все данные находятся в памяти приложения. Давайте сохраним их на сервер:
portal.save();

Вы ожидали что-то большее? AJAX? Одной строчкой мы отправляем запрос на сервер. Помните, что тип запроса меняется: если вы создаете новый объект, то будет отправлен POST запрос, иначе будет PUT.
Я кратко затронул модели. Backbone дает значительно больше возможностей по работе с моделями. Подробности можно найти в документации.

Коллекции

Коллекции в Backbone это просто набор моделей. По аналогии с базой данных коллекции — это результаты запросов к БД, содержащие строки (Модели).
Создадим коллекцию игр:
var GamesCollection = Backbone.Collection.extend({
    model : Game
});

Первое, что нужно заметить, мы определяем из каких моделей состоит коллекция. Мы создали коллекцию игр, состоящую из моделей Игра.

Теперь вы можете работать с данными. Для примера, давайте расширим коллекцию, добавив метод, определяющий старые игры.
var GamesCollection = Backbone.Collection.extend({
  model : Game,
  old : function() {
    return this.filter(function(game) {
      return game.get('releaseDate') < 2009;
    });
  }
});

Просто, не правда ли? Мы просто проверяем те игры, которые были созданы до 2009 года и возвращаем их. Также вы можете управлять коллекцией напрямую:
var games = new GamesCollection
games.get(0);

Пример выше создает новую коллекцию и получает модель с ID 0. Вы можете найти элемент в определенной позиции через ссылку на позицию объекта: games.at(0);

И, наконец, вы можете динамически пополнять вашу коллекцию вот так:
var GamesCollection = Backbone.Collection.extend({
  model : Game,
  url: '/games'
});

var games = new GamesCollection
games.fetch();


Мы оповещаем Backbone с помощью какого url управлять данными. Дальше мы просто создаем новый объект и вызываем метод fetch, который приводит к асинхронному запросу данных с сервера и заполняет коллекцию.

Теперь вы знаете основы коллекций Backbone. Как я отметил выше, существуют ещё тонны полезных вещей, которые умеет Backbone, например все методы библиотеки Underscore. Ознакомьтесь с документацией перед началом экспериментов.

Вид

На первый взгляд виды могут показаться запутанными. В отличии от чистого MVC, виды в Backbone имеют часть функций контроллера.

В обязанности видов входит:
  • Слушание событий DOM, моделей и коллекций.
  • Представление состояния приложения (отрисовка вида).
Давайте создадим простой вид:
var GameView = Backbone.View.extend({
  tagName: "div",
  className: "game",
  render: function() {
    // код отрисовывающий вид
  }
});

Все довольно просто. Я просто определяю, какой HTML элемент должен использоваться для обертки вида (tagName и className).

Следующий код отрисовывает вид:
  render : function() {
    this.el.innerHTML = this.model.get('name');

    // Или используя jQuery:
    $(this.el).html(this.model.get('name'));
  }

el ссылается на DOM элемент, который представляет этот вид. Мы просто кладем имя игры в HTML элемент. Очевидно, что используя jQuery все немного проще.

В более сложных разметках использование HTML кода внутри JavaScript утомительно и бессмысленно. В этих случаях стоит применять шаблоны.

Backbone имеет собственный шаблонизатор (часть Underscore.JS) но вы свободны применять любой вид шаблона.

Наконец, давайте посмотрим, как виды слушают события. Сначала DOM события.
events: {
    'click .name': 'handleClick'
},

handleClick: function(){
    alert('In the name of science... you monster');
    // Other actions as necessary
}

Конечно, не так как привычно как у jQuery, но тоже просто. Мы определяем, какие события слушаем через объект Events. Как видно из кода выше, первая часть ссылается на событие, а следующая определяет функцию, которая связана с событием.

Теперь свяжем наш вид с моделью:
var GameView = Backbone.View.extend({
    initialize: function (args) {
        _.bindAll(this, 'changeName');
        this.model.bind('change:name', this.changeName);
    }
});

Первое, что нужно отметить — это как мы располагаем наш связывающий код в функции initialize. Я считаю, что initialize — это наилучшее место для привязки событий.

bindAll это метод Underscore, который связывает контекст this с функцией. Это особенно полезно в событиях.

Теперь как только имя модели будет изменено, вызовется функция changeName. Вы можете использовать другие префиксы вместо change: для опроса состояния модели.

Контроллер

Контроллеры позволяют создавать приложение, запоминающие свое состояние в url hash (hashbangs).
var Hashbangs = Backbone.Controller.extend({
    routes: {
        "!/":      "root",
        "!/games": "games",
    },
    root: function() {
        // Подготавливаем начальную страницу и другое
    },

    games: function() {
        // Перерисовка видов в коллекции игр
    }
});

Это очень похоже на роуты в традиционных серверных MVC фрэймворках. Например, !/games будет связан с функцией в то время как URL в браузере будет domain/#!/games.

Используя hashbangs, вы можете создавать веб-приложения, которые запоминают свое состояние и могут индексироваться Google.

Если вы боитесь, что это сломает кнопку Назад, то Backbone может позаботиться об этом.
// Создание контроллера
var ApplicationController = new Controller; 

Backbone.history.start();

Backbone следит за изменением #hash и оповещает контроллеры.

Теперь вы знаете основы Backbone, попробуем создать тестовое приложение.

Пример: Список Todo


Это приложение было создано Jérôme Gravel-Niquet. Оно использует простой localStorage адаптер для сохранения ваших данных в браузере (не останавливаюсь на адаптере, посмотрите его код — там все предельно просто). Посмотрите, что получится в конце, чтобы лучше понять код: Список Todo

Модель Туду

Наша базовая модель описывает элемент туду списка, который имеет атрибуты content, order и done.
  window.Todo = Backbone.Model.extend({

    // Если вы не написали текст, это будет заглушкой
    // Это немного другой подход, в начале этой статьи мы использовали default
    EMPTY: "empty todo...",

    // Если модель не имеет `content`, подсовываем по умолчанию
    initialize: function() {
      if (!this.get("content")) {
        this.set({"content": this.EMPTY});
      }
    },

    // Переключаем статус `done`
    toggle: function() {
      this.save({done: !this.get("done")});
    },

    // Удаляем из localStorage и удаляем вид
    clear: function() {
      this.destroy();
      this.view.remove();
    }

  });


Коллекция Туду

Коллекция туду хранится в localStorage
  window.TodoList = Backbone.Collection.extend({

    // Эта коллекция будет состоять из моделей Todo
    model: Todo,

    // Сохраняем все туду под неймспейсом "todos" в localStorage
    localStorage: new Store("todos"),

    // Фильтр для получения списка тудушек, которые завершены
    done: function() {
      return this.filter(function(todo){ return todo.get('done'); });
    },

    // Фильтр для получения списка тудушек, которые не завершены
    remaining: function() {
      return this.without.apply(this, this.done());
    },

    // Мы сохраняем наши туду последовательно, в то время когда в базе они могут храниться хаотично.
    // В нашем случае мы используем GUID в качестве ключа. Этот метод получает следующий ид объекта.
    nextOrder: function() {
      if (!this.length) return 1;
      return this.last().get('order') + 1;
    },

    // Туду отсортированы по порядку добавления в список
    comparator: function(todo) {
      return todo.get('order');
    }

  });

  // Создадим глобальную коллекцию тудушек
  window.Todos = new TodoList;


Вид — Элемент туду

  // DOM Элемент для туду
  window.TodoView = Backbone.View.extend({

    // это элемент списка
    tagName:  "li",

    // Кэшируем шаблон
    // Код шаблона ниже в статье
    template: _.template($('#item-template').html()),

    // События DOM, которые связаны с туду
    events: {
      "click .check"              : "toggleDone",
      "dblclick div.todo-content" : "edit",
      "click span.todo-destroy"   : "clear",
      "keypress .todo-input"      : "updateOnEnter"
    },

    // TodoView слушает изменения модели и перерисовывает себя.
    // так как эта связь один-вид-одна-модель, то мы просто устанавливаем 
    // связь с моделью напрямую.
    initialize: function() {
      _.bindAll(this, 'render', 'close');
      this.model.bind('change', this.render);
      this.model.view = this;
    },

    // Перерисовываем содержимое
    render: function() {
      $(this.el).html(this.template(this.model.toJSON()));
      this.setContent();
      return this;
    },

    // Для избежания XSS мы используем `jQuery.text` для изменения контента туду
    setContent: function() {
      var content = this.model.get('content');
      this.$('.todo-content').text(content);
      this.input = this.$('.todo-input');
      this.input.bind('blur', this.close);
      this.input.val(content);
    },

    // Переключаем состояние "done" у модели
    toggleDone: function() {
      this.model.toggle();
    },

    // Переключаем вид в режим редактирования
    edit: function() {
      $(this.el).addClass("editing");
      this.input.focus();
    },

    // Закрываем режим редактирования, сохраняем изменения
    close: function() {
      this.model.save({content: this.input.val()});
      $(this.el).removeClass("editing");
    },

    // Если нажать `enter`, то туду сохранится
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },

    // Удаление DOM элемента
    remove: function() {
      $(this.el).remove();
    },

    // Удаление элемента и модели
    clear: function() {
      this.model.clear();
    }

  });


Вид — Приложение

Это базовый вид нашего приложения
  window.AppView = Backbone.View.extend({

    // Вместо того, чтобы создавать новый элемент привяжемся к существующему HTML скелету
    el: $("#todoapp"),

    // Шаблон для статистики
    // Код шаблона ниже в статье
    statsTemplate: _.template($('#stats-template').html()),

    // Составляем список событий для создания новых туду, очистки завершенных
    events: {
      "keypress #new-todo":  "createOnEnter",
      "keyup #new-todo":     "showTooltip",
      "click .todo-clear a": "clearCompleted"
    },

    // При инциализации мы начинаем слушать определенные события коллекции:
    // элементы изменены, добавлены, загружены. Тут же мы загружаем туду, которые были
    // сохранены в localStorage
    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render');

      this.input    = this.$("#new-todo");

      Todos.bind('add',     this.addOne);
      Todos.bind('refresh', this.addAll);
      Todos.bind('all',     this.render);

      Todos.fetch();
    },

    // Перерисовка приложение - обновление статистики. Остальное не меняется.
    render: function() {
      var done = Todos.done().length;
      this.$('#todo-stats').html(this.statsTemplate({
        total:      Todos.length,
        done:       Todos.done().length,
        remaining:  Todos.remaining().length
      }));
    },

    // Создание элемента туду. Создаем вид и засовываем в `<ul>`
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

    // Отрисовываем все элементы
    addAll: function() {
      Todos.each(this.addOne);
    },

    // Создаем атрибуты для новых туду
    newAttributes: function() {
      return {
        content: this.input.val(),
        order:   Todos.nextOrder(),
        done:    false
      };
    },

    // Если нажать return в поле ввода имени туду - создастся новая модель.
    // Создание модели вызовет определенные события которые по цепочке отрисуют новый элемент
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      Todos.create(this.newAttributes());
      this.input.val('');
    },

    // Удаляем все завершенные туду, удаляем их модели.
    clearCompleted: function() {
      _.each(Todos.done(), function(todo){ todo.clear(); });
      return false;
    },

    // Показываем тултип после одной секунды ожидания.
    showTooltip: function(e) {
      var tooltip = this.$(".ui-tooltip-top");
      var val = this.input.val();
      tooltip.fadeOut();
      if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout);
      if (val == '' || val == this.input.attr('placeholder')) return;
      var show = function(){ tooltip.show().fadeIn(); };
      this.tooltipTimeout = _.delay(show, 1000);
    }

  });

  // Наконец - создаем наше приложение
  window.App = new AppView;

Оригинал с аннотациями можно посмотреть тут

HTML шаблоны и CSS

    <!--Код скелета приложения-->
    <div id="todoapp">

      <div class="title">
        <h1>Todos</h1>
      </div>

      <div class="content">

        <div id="create-todo">
          <input id="new-todo" placeholder="What needs to be done?" type="text" />
          <span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
        </div>

        <div id="todos">
          <ul id="todo-list"></ul>
        </div>

        <div id="todo-stats"></div>

      </div>

    </div>
    
    <!--Шаблон элемента туду-->
    <script type="text/template" id="item-template">
      <div class="todo <%= done ? 'done' : '' %>">
        <div class="display">
          <input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
          <div class="todo-content"></div>
          <span class="todo-destroy"></span>
        </div>
        <div class="edit">
          <input class="todo-input" type="text" value="" />
        </div>
      </div>
    </script>

    <!--Шаблон статистики приложения-->
    <script type="text/template" id="stats-template">
      <% if (total) { %>
        <span class="todo-count">
          <span class="number"><%= remaining %></span>
          <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
        </span>
      <% } %>
      <% if (done) { %>
        <span class="todo-clear">
          <a href="#">
            Clear <span class="number-done"><%= done %></span>
            completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span>
          </a>
        </span>
      <% } %>
    </script>

CSS не прикладываю, он очень длинный и совсем не по теме.

Все, наше тестовое приложение готово, результат можно посмотреть тут.

Чего полезного можно узнать из Backbone


Вот несколько уроков, которые вы можете выучить из архитектуры Backbone приложений:
  • Нам очень нужен MVC для интерфейса. Традиционные методы создают код, который очень сильно завязан на себя, он грязный и его сложно поддерживать.
  • Хранить данные и состояние в DOM это плохая идея. Это особенно остро ощущается, если вы создаете приложение, которому необходимо, чтобы различные части приложение обновлялись, используя одни данные.
  • Толстые модели и худые контроллеры — это хорошо. Процесс разработки значительно упрощается, когда бизнес-логика управляется моделями.
  • Шаблоны очень важны. Добавляя HTML внутрь вашего JavaScript вы получаете плохую карму.
Достаточно сказать, что Backbone меняет представление о создании интерфейсов, по крайней мере, для меня. Если у вас есть вопросы — задавайте.

Источники, которые были использованы при написании


Create JavaScript Apps with Backbone.js (Jim Hoskins)
Getting Started with Backbone.js (Siddharth)
Адаптер backbone-locaLstorage
Пример Todos (Jérôme Gravel Niquet)
Исходник Todos.js с аннотациями

Что ещё почитать


Документация по Backbone на GitHub
Документация по Underscore на GitHub
Node Tutorial Part 19: Backbone.js (Alex Young)
Создание большого приложения на Javascript (оригинал Addy Osmani, перевод trurl123)
Mikhail Davydov @azproduction
карма
449,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (47)

  • +12
    Сумасшедший объём кода для подобного todo-приложения, даже несмотря на то, что это пример.
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        да, оформлена документация кода и примера (todo) просто замечательно.
    • +1
      Это хорошо. Есть шанс, что при усложнении проекта количество кода будет расти не экспоненциально, а линейно.
  • +1
    Очень интересно. Я уже краем уха слыхал об этой библиотеке, но не пользовался. Недавно начал один небольшой проект, используя Sencha Touch, так там тоже сильная по возможностям MVC структура — очень понравилось манипулировать данными через модели, при этом каждая модель может сохранять данные разными способами.
    Я думаю, что такой способ работы с JS в веб-приложениях — это следующий этап качественного веба.
    Да и как-то православней, я считаю, обновлять данные через DOM, вместо того, чтобы сразу загружать данные с html кодом.
    • 0
      Мы тоже так думали. В конце концов отказались, т.к. отрисовка фронта с помощью шаблонов и подгрузкой данных с помощью JS превратилась в большие задержки на клиенте.
      + более сложная структура отображения фронта
      + больше кода.

      Но последние 2 пункта были в нагрузку. Не приемлимы были 2-5 секунды рендеринга страницы(и это без учета подгрузки данных с сервера) на очень хороших клиентских системах.

      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Элементов было около 25, но каждый из них являлась большой карточкой со множеством активных элементов, которые содержат в контекстном меню различные дополнительные данные.

          Опять же, писать lasy и генерировать все это с помщью JS, как оказалось не выгодно(с точки зрения скорости разработки)
    • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
    • +3
      Использую knockoutjs уже несколько месяцев и не могу нарадоваться.
      Понял что до этого страдал фигней :). Очень рекомендую всем.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          ну а что мешает использовать вместе?
          это немного из другой оперы. Тут манипуляция данными, а не dom-объектами, как в jQuery.

          knockoutjs выглядет очень интересно. Особенно для небольших приложений.
          • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Заинтриговали — полез смотреть
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        По мне так backbone сложнее и сильнее завязан на верстку.

        Например следующий код перестанет отрабатывать, как только ссылку заменят кнопкой или просто сменят название класса. Такой подход не далеко ушел от jQuery.delegate

        events: {
        «click .todo-clear a»: «clearCompleted»
        }

        В knockoutjs все это находится в темплейте, т.е. в одном месте.
        + Очень просто связывать элементы формы. Меняешь значения переменной в модели, автоматом обновляется значение в текстовом поле и наоборот.
  • +1
    Я не придираюсь, конечно, но то, что вы называете видом, правильнее называется Представление. Модель—Представление—Контроллер, MVC.

    За статью спасибо, разумеется.
    • 0
      Всегда думал, что это вид Модель-Вид-Контроллер — трудности перевода. Ещё MVC называют «Модель-представление-поведение». В тексте не буду уже менять.
      • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    > Backbone.js это каркас для создания RIA JavaScript приложений, его автором является Jeremy Ashkenas, создатель CoffeeScript, Backbone является частью компании Document Cloud ей же «принадлежит» Undescrore.js.

    Рискну кармой, но: извините, но я не смог этого понять.
    • +1
      Разработчики Document Cloud написали underscore.js, на его основе — Backbone, а еще CoffeeScript. Что тут непонятного?
      • 0
        Ну что фреймворк является частью компании как-то не по-русски звучит :)
  • +1
    отличная библиотека. Простая и мощная.
    для большого проекта лучше все разбивать на отдельные файлы/директории и собирать в объект App(App.Models.Apple, App.Collections.Apples,...), который потом бы инициализировал контроллер.

    также есть проект для поддержки взаимодействия в моделях
  • –6
    Spinefarm Records одобряет.
  • 0
    Хорошая статья, спасибо. Вот, к списку ссылок можно добавить: Building Single Page Applications With jQuery’s Best Friends, от Addy Osmani, там небольшую галерею строили с использованием Backbone.js
  • +2
    еще куча ссылок по backbone
    • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Спасибо, за статью.
    Сейчас плотно использую backbone для HTML5 приложения на iPad. В моем случае необходимо было сделать свой интерфейс не по гайдлайнам производителей. В целом очень понравилось.
    Еще бонус в том, что будучи верстальщиком, мне удобно и понятно работать с этой библиотекой, и не приходится отвлекать программистов. Еще не пробовал Sencha Touch и jQuery Touch — это будут следующие этапы :)
  • –1
    по-моему ужасно не читабельный код…
  • 0
    Откуда у модели this.view, а у представления this.model? Не понял, где и как осуществляется связывание модели с представлением. Я надеюсь, существует возможность связать с одной моделью несколько представлений?
    • 0
      // TodoView слушает изменения модели и перерисовывает себя.
          // так как эта связь один-вид-одна-модель, то мы просто устанавливаем 
          // связь с моделью напрямую.
          initialize: function() {
            _.bindAll(this, 'render', 'close');
            this.model.bind('change', this.render);
            this.model.view = this;
          },

      Как я понял все ноги отсюда растут. видимо _.bindAll() создаёт this.model в представление, а this.model.view = this создаёт this.view для модели, хотя не уверен что это хорошая практика для MVC (вернее уверен в обратном :) )

      А судя по «так как эта связь один-вид-одна-модель» возможность связывать есть вплоть до «многие-модели-многие-виды» иначе бы, наверное, и заморачиваться не стоило.
      • 0
        Присваивание this.model.view, признаюсь, проглядел. Но про _.bindAll() очень сомнительно (из топика: «bindAll это метод Underscore, который связывает контекст this с функцией. Это особенно полезно в событиях.»). Навряд ли этот bindAll (который к тому же часть underscore, а не backbone) знает что-либо про view и модели.
        • 0
          Глобальный объект похоже, может практически всё знать о приложении. Но в библиотеках JS не силён, потому только предположение.
  • 0
    Сейчас все данные находятся в памяти приложения. Давайте сохраним их на сервер:
    portal.save();

    Вы ожидали что-то большее? AJAX? Одной строчкой мы отправляем запрос на сервер. Помните, что тип запроса меняется: если вы создаете новый объект, то будет отправлен POST запрос, иначе будет PUT.

    А по какому урлу будет идти сохранение?

    Тут мелькнула мысль, что Backbone может очень легко интегрироваться с CouchDB без какой-либо серверной прослойки и чуть-ли не без «выделенного» веб-сервера. Погуглил и оказалось что мысль не только у меня мелькнула, да не только мелкнула, но и реализовалась — github.com/janmonschke/backbone-couchdb. Ещё реализовать аутентификацию и авторизацию и почти готовая среда для разработки двухзвенных приложений.

    Спасибо за статью.
    • 0
      var GamesCollection = Backbone.Collection.extend({
        model : Game,
        url: '/games' // <<<
      });
      • 0
        За намёк спасибо. Хотя пример с portal.save(); до введения в коллекции у вас, потому и спросил. Видимо надо читать доки на предмет является ли коллекция обязательным отношением к модели и как формируется id (тоже прямо не указано).
  • 0
    Скажите пожалуйста где именно происходит связывание вида с моделью. В этом примере мне не понятно что такое this.model.
    var GameView = Backbone.View.extend({
        initialize: function (args) {
            _.bindAll(this, 'changeName');
            this.model.bind('change:name', this.changeName);
        }
    });
    • 0
      Контроллер связывается с моделью при инициализации контроллера. В том пред примере на самом деле не показано откуда берется this.model
      Если посмотреть на «Пример: Список Todo», то все становится понятнее:
      window.TodoView = Backbone.View.extend({
      // ...
      });
      
      window.AppView = Backbone.View.extend({
      // ...
      
      // Создание элемента туду. Создаем вид и засовываем в `<ul>`
          addOne: function(todo) {
            var view = new TodoView({model: todo}); // <<<
            this.$("#todo-list").append(view.render().el);
          }
      
      //...
      });
  • 0
    Сам, недавно столкнулся с необходимостью превозмочь Backbone, сразу вспомнил про этот топик и решил от него оттолкнуться, но в результате всё пришлось понимать самому. За день я таки разобрался, что к чему, но на самом деле с контроллером всё в разы проще.
    Как по мне то пример с тудушками с backbon'овского туториала не самый удачный, не затронут аспект контроллера, и его возможность работать с routes. Да и как основное приложение контроллер был бы более верным решением, т.к. через него более прозрачно проистекают все маннипуляции с данными и рендером вьюшек.
    • 0
      Да и забыл спросить. Это у меня что то неправильно с руками или это у backbone внутренний механизм такой? я заметил что если у AplicationView не определён элемент el : "какой то контейнер", то у этой вьюхи никогда не происходит вызов render(). Я сколько не дебажил, пока не прописывал в классе вьюхи что то типа el: 'body' в консоли не прописывалось моё сообщение element rendered.
  • 0
    Хранить this.view у модели — плохой паттерн, надо bind делать у view на прослушку событий (от коллекций и моделей)
  • 0
    А как происходит связывание вида с моделью?
    Везде во view вызовы this.model, но не могу найти код, где вид узнает о своей модели.
    • 0
      Вот тут
      new TodoView({model: todo});
  • 0
    Может кому пригодится, вполне адекватный перевод книги 'Developing Backbone.js Applications' — github.com/AlexFadeev/backbone-fundamentals
    • 0
      Переведено немного, но хорошо.
  • –1
    «Сложных приложений» — издеваетесь?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.