среда, 10 июля 2013 г.

Backbone.js Полная подсказка с реальными примерами

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/underscore.js" type="text/javascript"></script>
<script src="js/backbone.js" type="text/javascript"></script>
<script type="text/javascript">

// ==================================================
// Метод extend() позволяет наследовать и расширять любые объекты.

// ==================================================

// MODEL

var Model = Backbone.Model.extend({
      // Значение полей модели по умолчанию. Эти значения берутся, если при создании модели в неё не передали какие-либо свойства options.
      defaults: {
          name: 'Boris'
        , age: 21
      }
     
    , id: 'modelID' // Это специальное свойство каждой модели - обычная произвольная строка. Данная модель может быть вытащена из Collection по данному id напряму. id используется по умолчанию для генерации URL при отправке запросов на сервер для получения данных для модели.
   
    , idAttribute: '_id' // Это уникальный идентификатор модели, который используется для связывания данных напрямую с базами данных MongoDB и CouchDB.
   
    //, cid: '' // Client ID - это специальное свойство модели - уникальный идентификатор, автомтически назначающийся всем моделям в момент создания их объекта-экземпляра. cid'ы удобны , когда модель еще не была сохранена на сервере и не имеет своего настоящего id, но уже должна быть отображена на странице. cid обычно принимают вид c1, c2, c3 и так далее.
     
    , validate: function(attrs) { // Метод валидации данных модели. Вызывается перед методом set(). Код в данном методе может быть абсолютно любой.
        if (attrs.name === undefined) {
            return 'Error! Remember to set a name';
        }
    }
     
    , initialize: function(options) {
            // Любые действия при создании объекта Model.
            // alert('Model init');
           
            this.on('change:name', function(){
                var name = this.get('name');
                console.log('Name updated to ' +  name);
            });
      }
     
    // , url: function () { // Генерирует относительный URL-адрес по которому модель запрашивает данные с сервера для своего обновления при синхронизации.
    //     return this.urlRoot() + '/' + this.get('name');
    // }
     
    // Укажите urlRoot, если вы используете модель вне Collection.  
    , urlRoot: '/get/model/data' // URL-адрес по которому модель запрашивает данные с сервера для своего обновления при синхронизации.
    // appModel.urlRoot(); // urlRoot также может быть и функцией.
    // , urlRoot: function () {
    //     return this.repo.url() + '/issues';
    //  }

    , parse: function(response) { // Данный метод вызывается всякий раз, когда данные для модели возвращаются сервером в методах fetch() и save(). Реализация по умолчанию просто перебрасывает JSON-ответ. Переопределите этот метод, если вы хотите оборачивать ответ в другое пространство имен или для обеспечения его работы с уже существующим сторонним кодом.
          return response.data;
      }

});

var appModel = new Model({name: Doris, age: 23});

// appModel.get('name'); // Получить значение какого-либо свойства из модели.
// appModel.escape('name'); // Получить обработанное HTML-безопасное значение свойства из модели для предотвращения XSS.

// appModel.set({'name': 'Doris'}); // Установить новое значение какому-либо свойству в моделе.
// Сработает событие on('change') в модели, если вы не передали опцию silent: true
// appModel.set({'name': 'Doris', 'age': 23}, {silent: true}); // Значение изменится, но событие on('change') не сработает.

// appModel.unset('name'); // Удаляет данное свойство из состава модели. Запускает событие on('change'), если не передана опция {silent: true}

// appModel.clear(); // Удаляет все свойства из модели. Запускает событие on('change'), если не передана опция {silent: true}

// if (appModel.has('name')) {} // Проверит имеет ли данная модель данное свойство, если оно не установлено в null или undefined.

// appModel.attributes; // Получить перечень всех свойств модели.

// appModel.toJSON(); // Возвращает копию поля attributes для преращения его в дальнейшем в JSON-строку методом JSON.stringify()

// appModel.changed; // Получить перечень всех свойств модели, которые были изменены с тех пор как последний раз сработало событие change.

// appModel.fetch(); // Отправить запрос на сервер для получения данных для модели по заданному в модели url и вставить их в модель. Обновление данных происходит через автоматический вызов функции Backbone.sync(); Событие on('change') срабатывает, если полученные данные отличаются от исходных, находящихся в модели в данный момент. Метод принимает функции-коллбэки success и error. appModel.fetch({success() function(){...}, error: function(){ ... }});

// appModel.save(); // Сохраняет данные из модели в базе данных через автоматический запуск функции Backbone.sync(); Вызов метода save() с новыми атрибутами приведет к срабатыванию события on('change') и события sync() после того, как сервер известит браузер об успешном выполнении сохранения. Передайте {wait: true}, если хотите подождать ответа сервера, прежде чем устанавливать новые значения свойств в моделе. Метод принимает функции-коллбэки success и error.
// appModel.save({name: 'Doris'}, {success() function(){...}, error: function(){ ... }});
// Можно сохранять только конкретные поля модели. appModel.save({name: 'Doris'});

// appModel.destroy(); // Уничтожает модель на сервере, втоматически вызывая функцию Backbone.sync(); Метод принимает функции-коллбэки success и error. appModel.destroy({success() function(){...}, error: function(){ ... }}); Модель удалится из всех коллекций, содержащих её, после того, как сервер известит браузер об успешном удалении модели.

// if (appModel.isValid()) {} // Проверяет не сломалась ли модель. Валидны ли в ней данные?

// appModel.url() // Генерирует относительный URL-адрес по которому модель запрашивает данные с сервера для своего обновления при синхронизации.

// appModel.clone(); // Возвращает новый экземпляр модели с идентичными свойствами.

// if (appModel.isNew()) {} // Проверяет была ли модель уже сохранена на сервер. Модель считается новой, если она еще не имеет свойства id.

// appModel.change(); // Ручной запуск события "change" и "change:attribute" для каждого измененного атрибута данной модели. Если вы передавали в модель атрибуты с опцией {silent: true} внутри метода set(), то в конце нужно вызвать метод appModel.change();.

// if (appModel.hasChanged()) {} // Проверяет была ли модель изменнена с последнего события change. Если в hasChange() подставлено свойство модели, то она возвращает true, если данное свойство в модели было изменено. Данный метод используется внутри метода обработки события on('change').
// Пример
// book.on("change", function() {
//     if (book.hasChanged("title")) {
//         ...
//     }
// });

// appModel.changedAttributes(); // Возвращает хэш-массив только тех свойств модели, которые были изменены.

// appModel.previous(); // Во время возникновения события change этот метод позволяет получить предыдущее значение данного свойства модели.
// Пример
// var bill = new Backbone.Model({
//   name: "Иван Петров"
// });
//
// bill.on("change:name", function(model, name) {
//   alert("Изменено имя с " + bill.previous("name") + " на " + name);
// });
//
// bill.set({name : "Иван Иванов"});

// appModel.previousAttributes(); // Возвращает копию предыдущих свойств данной модели. Используется для определения различий между двумя версиями модели или вернуть валидное состояние после ошибки.

// Для изменения View через Model лучше использовать метод on()
// Пример modelA.on('change', view.renderA, view);

// ==================================================

// COLLECTION

var Collection = Backbone.Collection.extend({
   
      model: appModel // Определяем класс моделей, которые будут находится в коллекции.
     
    , initialize: function () {
            // Любые действия при создании объекта Collection.
            // alert('Collection init');

     }
   
    , comparator: function () {} // Метод, который предназначен для сортировки моделей в коллекции, он вызывается при сортировки моделей в коллекции через метод sortBy() из Underscore.js или Array.sort().
   
    , parse: function(response) { // Метод вызывается каждый раз когда данные для моделей из коллекции приходят с сервера при выполнения метода fetch() Реализация по умолчанию просто перебрасывает JSON-ответ. Переопределите этот метод, если вы хотите оборачивать ответ в другое пространство имен или для обеспечения его работы с уже существующим сторонним кодом. Если в моделях определен свой метод parse(), то он будет вызван для каждой модели после выполнения метода parse() для всей коллекции.
          return response.results;
      }
   
    , url: '/notes' //Свойство или функция url() указывает адрес положение данных для коллекции на сервере. Модели внутри коллекции с определенным url будут использовать его, чтобы конструировать свои собственные url.
   // url: function() {
   //     return this.document.url() + '/notes';
   // }
   
});

var appCollection = new Collection();

// На Collection можно навесить любые слушатели событий on('change', 'add', 'remove', 'fetch'), чтобы получать оповещения, когда любая модель из коллекции изменяется. Любое событие которое сработает в модели в коллекции также сработает и напрямуя в коллекции. Это позволяет слушать изменение значенией свойств в любой модели коллекции.

// appCollection.on('add', function(model) { // Метод позволяет навешивать события на коллекцию.
//    console.log('I liked ' + model.get('name') + ' its this one, right? ' + mode.get('age'));
//});

// appCollection.get(); // Возвращает модель из коллекции по её id.
// Пример
// var user = appCollection.get(110);

// appCollection.getByCid (); // Возвращает модель из коллекции по указанному CID. Это свойство модели автоматически назначается ей при её создании. Находит применение для моделей, которые еще не были сохранены на сервер и потому пока еще не имеют настоящего id.

// appCollection.models; // Прямой доступ к массиву моделей в коллекции. Совместно с этим далее пишутся методы .get(), .at() и методы Underscore.js, чтобы получить конкретные модели из коллекции.

// appCollection.where(); // Возвращает массив моделей из коллекции, подхлдящий под переданный хэш-массив свойств. Удобно для проведения простой фильтрации.

// appCollection.at(); // Возвращает модель из коллекции по индексу. Полезно, если ваша коллекция отсортирована, если нет, то этот метод возвращает модели по порядку их добавления в коллекцию.

// appCollection.pluck(); // Собирает значения конкретного свойства из каждой модели в коллекции. Эквивалентно выхову метода map() с возвратом одного атрибута из итератора.

// appCollection.create(); // Создает модель внутри коллекции. Вызов этого метода эквивалентен созданию экземпляра модели с хэш-массивом её свойств, сохранению модели на сервер и  добавления данной модели в коллекцию. Метод вовращает модель или false, если ошибка валидации не позволила модели создастся. Создание модели автоматически запускает событие on('add') и событие 'sync' как только модель успешно создастся на сервере. Передайте методу опцию {wait: true}, если вы хотите подождать ответа от сервера перед добавлением модели в коллекцию.

// appCollection.add(); // Добавляет модель или массив моделей в данную коллекцию. Сработает событие on('add') в коллекции, если вы не передали опцию {silent: true} Чтобы вставить модель по специальному индексу передайте опцию {at: some_index}. В этом случае в обработчик события on('add') в параметра options.index будет передан индекс, под которым была вставлена модель.

// appCollection.push(); // Добавляет модель в конец коллекции. Принимает теже аргументы, что и метод add().

// appCollection.unshift(); // Добавляет модель в начало коллекции. Принимает теже аргументы, что и метод add().

//  appCollection.remove(); // Удаляет модель или массив моделей из коллекции. Сработает событие on('remove') в коллекции, если вы не передали опцию {silent: true} В обработчик события on('remove') в параметра options.index будет передан индекс, под которым была удалена модель из коллекции.

// appCollection.pop(); // Удаляет последнюю модель из коллекции. Принимает теже аргументы, что и метод remove().

// appCollection.shift(); // Удаляет первую модель из коллекции. Принимает теже аргументы, что и метод remove().

// appCollection.length; // Получить число моделей, которые сейчас находятся в коллекции.

// appCollection.toJSON(); // Возвращает хэш-массив, содержащие свойства каждой модели из коллекции. Используется для сериализации и сохранения коллекции целиком.

// appCollection.fetch(); // Метод получает дефолтный набор моделей для данной коллекции с сервера. Обновление данных происходит через автоматический вызов функции Backbone.sync(); Метод принимает функции-коллбэки success и error. appCollection.fetch({success() function(){...}, error: function(){ ... }}); Когда данные для моделей из коллекции прийдут с сервера будет вызван метод on('reset') для коллекции. Серверный обработчик запросов fetch() должен возвращать JSON-массив моделей. Данные в коллекции можно не заменять, а добавлять пришедшие модели в коллекции. Для этого надо добавить опцию {add: true}. Опции jQuery.ajax также могут быть напрямую переданы в метод fetch() для получения определенной страницы пагинированной коллекции. appCollection.fetch({data: {page: 3}})
// Метод fetch() не должен использоваться для наполнения коллекции на этапе загрузки страницы. Все модели, необъодимые для загрузки, должны быть предзагружены. Метод fetch() предназначен для загрузки моделей, которые не нужны немедленно.

// appCollection.reset(); // Метод полностью заменяет все содержимое коллекцией новым массивом моделей или хэш массивом совйств моделей. В конце сработает событий on('reset') коллекции, если не подавить его с помощью опции {silen: true}. Вызов метода reset() без аргументов - удобный способ опустошить всю коллекцию, то есть он сделает всю коллекцию пустой.

// appCollection.sort(); // Заставляет выполнить пересортировку моделей в коллекции. Обычно не требуется вызывать этот метод вручную, так как коллекции с описанным методом comparator() всегда сами будут себя автоматически отсортировывать при внесение изменений в коллекцию. Вызов метода sort() запускает срабатывания события on('reset'), если не подавить его с помощью опции {silen: true}.

// Методы Underscore.js использующиеся только в Collection для фильтрации моделей:

// forEach (each)
// map
// reduce (foldl, inject)
// reduceRight (foldr)
// find (detect)
// filter (select)
// reject
// every (all)
// some (any)
// include
// invoke
// max
// min
// sortBy
// groupBy
// sortedIndex
// shuffle
// toArray
// size
// first
// initial
// rest
// last
// without
// indexOf
// lastIndexOf
// isEmpty
// chain

// ==================================================

// VIEW

var View = Backbone.View.extend({
   
    // Задаем внешний тэг, его id и class для вставки в него содержимого View.
       tagName: 'div'
     , id: 'main'
     , className: 'block'
    // , attributes: 'value="main"' // Атрибуты, которые будут прописаны внутри тэга, в который будет вставлен наш View.
   
    // , el: $('#content') // Ссылка на элемент в HTML-коде страницы, куда будет вставлен наш View.
   
    // this.el создается из свойств tagName, id, className и attributes, если они указаны.
    // Если ничего не указано, то el будет пустым <div></div>
   
    // $el - это удобная замена конструкция вида $(this.el), что позволяет сразу использовать jQuery для работы с элементом.
   
    // , model: appModel // Связываем View с конкретной Model
   
      , initialize: function () {
            // Любые действия при создании объекта View.
            // alert('View init');
           
            // Взаимодействие с событиями привязаной к данному View модели Model
            // this.model.on('change', this.render);
            // this.model.on('destroy', this.remove);
      }
     
    , events: { // События привязанные при взаимодействии с содержимым View.
              'click .icon': 'openItem'
            , 'click .delete': 'deleteItem'
      }
      // Для прослушивания Model через View лучше использовать метод listenTo()
      // Пример view.listenTo(modelA, 'change', view.render);
     
    , openItem: function (event) {alert('Open item');}
    , deleteItem: function (event) {alert('Delete item');}
     
    , template: '<span class="icon">Open</span> Hello world! <span class="delete">Delete</span>'
     
    , render: function (options) { // HTML-код для отрисовки нашего View.
            this.$el.html(this.template);
            return this; // Обязательно возвращаем this.
      }

});

// var footer = $('#footer');
// appView.setElement(footer); // Изменение элемента, в который будет вставлен наш View после render.
// В этом случае $el будет ссылаться на footer и все события events будут привязаны уже к нему.

// appView.remove(); // Удаление нашего View из HTML-кода страницы

// ==================================================

// ROUTER

// Список URL, используемых в приложение.
// Изменение URL производит вызов соотвествующей функции контроллера.

var Router = Backbone.Router.extend({
      routes: {
              '': 'main'
            , 'about': 'about'
            , 'contacts/:name': 'contacts' // или 'contacts/*something' - любые символы после * попадут в функцию контроллера.
      }
   
    , main: function () {        
            var appView = new View(); // Создаем наш View.
            $('#content').html(appView.render().el); // Выводим наш View на экран.
      }
   
    , about: function () {
            $('#content').html('About content');
      }
   
    , contacts: function (name) {
            $('#content').html('Contacts content' + ' ' + name);
      }
     
    , initialize: function (options){
            // Любые действия при создании объекта Router
            // alert('Route init');
           
            // Динамическое добавление новых маршрутов после создания объекта.
            this.route('page/:number', 'page', function (number) {
                $('#content').html('Page content' + ' ' + number);
            });
           
            this.route(/^page\/([0-9])\/open$/, 'open');
      }
     
    , open: function (id) {// id - это то, что в регулчярном выражении написано внутри скобок.
            $('#content').html('Page content number' + ' ' + id);
      }
});

var appRouter = new Router();

// Прослушивание событий Router при переходе по URL.

appRouter.on('route:contacts', function (name) {
    alert(name);
});

// Добавление нового маршрута в Router.

appRouter.route('new', 'new', function () {
    $('#content').html('New content');
});

// ==================================================

// START ROUTER HISTORY

$(document).ready(function () {
   
    Backbone.history.start(); // Обязательно запустить отслеживание изменения URL после DOM ready.
    // Backbone.history.start({
    //       pushState: true, - браузер поддерживае HTML 5 pushState.
    //     , root: "/public/search/" - задаем корневой URL для Router, начиная с которого ставится символ #.
    //     , silent: true - если не хотим, чтобы начальный URL сработал, когда стартует Backbone.history.
    // })
   
    // Осуществление перехода по нужному адресу в Router
    appRouter.navigate('about', {
          trigger: true // Разрешить запустить функцию контроллера, связанную с данным маршрутом.
        , replace: true // Обновить URL в адресной строке без создания записи о переходе в истории браузера.
    });

});

</script>
<title>Backbone</title>
</head>
<body>
<ul>
    <li><a href="#">Main</a></li>
    <li><a href="#about">About</a></li>
    <li><a href="#contacts/boris">Contacts</a></li>
 </ul>
 <div id="content"></div>
 <div id="footer"></div>
</body>
</html>

3 комментария:

  1. А где подсветка синтаксиса, код вообще не читаем... Хотя бы сделали его моноспейсным шрифтом...

    ОтветитьУдалить
  2. Прошу меня извинить. В будущем добавлю подсветку кода.

    ОтветитьУдалить
  3. Doris в ковычки. очень хороший пример. только за 2 года автор так и не сделал подсветку

    ОтветитьУдалить