Данная статья является вольным переводом. Оригинал тут.
Думаю, ни для кого не секрет, что клиентские приложения в современных веб-сервисах становится все сложнее и количество JS кода в них растет. До недавнего времени архитектура клиентской части, как правило, разрабатывалась с нуля и была специфична для каждого проекта. Не удивительно что приходилось снова и снова сталкиваться с типичными задачами.
К MVC-фреймворкам на серверной стороне все уже привыкли, но JS код на клиенте часто бывает плохо структурирован.
Предлагаю ознакомиться с решением на базе backbone.js, underscore.js и jQuery, которое поможет решить эту проблему.
Каким бы мы хотели видеть наше приложение? Вот основные моменты, которые мне кажутся важными:
Попробуем решить эти задачи на примере простого приложения «Каталог фильмов».
Нам потребуется:
Задача данного фреймворка не в том чтобы дать вам кучу виджетов, и даже не в том, чтобы обеспечить уровень представления (view). Его задача дать вам несколько ключевых объектов, которые помогут структурировать код.
Нам понадобятся объекты Model, Collection, View и Controller.
Для получения полнофункциональной модели достаточно написать всего одну строчку кода:
Теперь мы можем получить экземпляры объекта, задавать и получать произвольные атрибуты:
Также можно передавать атрибуты напрямую в конструктор при создании объекта:
Выполнить какие-то проверки или иные действия при создании объекта, можно расширив модель функцией
initialize(). При создании объекта она будет вызвана с параметром, который вы передали в конструктор.
Также можно определить метод validate(), он будет вызываться каждый раз, когда вы задаете атрибуты и используется для валидации атрибутов. В случае если этот метод что-либо возвращает, атрибут не устанавливается:
Для более полного ознакомления с возможностями backbone предлагаю ознакомиться с документацией.
Коллекция в backbone представляет из себя упорядоченный список моделей некоторого типа. В отличие от обычного массива, коллекции обеспечивают гораздо больше функционала, такого как, например, установка правил сортировки с помощью метода comparator().
После того как определен тип модели в коллекции, добавление туда объекта выглядит чрезвычайно просто:
В общих чертах, представления backbone определяют правила отображения изменений модели в браузере.
Здесь начинаются манипуляции с DOM и в игру вступает jQuery. Для изначальной загрузки моделей в DOM нам потребуется шаблонизатор, мы воспользуемся связкой ICanHaz.js + mustache.js
Вот пример представления для нашего приложения:
До сих пор мы говорили о разных частях приложения, теперь посмотрим как объединить их в одно целое.
В контроллере мы свяжем все части приложения, а также определим пути для манипуляций с объектами и связанные с ними методы.
Здесь мы видим, что в контроллере сохраняется модель приложения, которая будет хранить все остальные модели и коллекции, а также представление приложения.
Модель приложения в нашем случае будет хранить коллекцию фильмов:
Представление приложения будет выглядеть так:
Ну и собственно индексный файл со всеми зависимостями и шаблонами:
Все приложение готово. Конечно, это только очень малая часть тех возможностей которые предоставляют данные библиотеки, но думаю, что данного примера достаточно, что почувствовать вкус к разработке, с помощью этих инструментов.
Введение
Думаю, ни для кого не секрет, что клиентские приложения в современных веб-сервисах становится все сложнее и количество JS кода в них растет. До недавнего времени архитектура клиентской части, как правило, разрабатывалась с нуля и была специфична для каждого проекта. Не удивительно что приходилось снова и снова сталкиваться с типичными задачами.
К MVC-фреймворкам на серверной стороне все уже привыкли, но JS код на клиенте часто бывает плохо структурирован.
Предлагаю ознакомиться с решением на базе backbone.js, underscore.js и jQuery, которое поможет решить эту проблему.
Постановка задачи
Каким бы мы хотели видеть наше приложение? Вот основные моменты, которые мне кажутся важными:
- Должен быть удобный способ описать модели нашей предметной области.
- Любые изменения в модели должны немедленно отражаться в пользовательском интерфейсе, если модель в нем представлена каким-либо образом.
- Понятная и легко-поддерживаемая структуризация кода в стиле MVC.
Попробуем решить эти задачи на примере простого приложения «Каталог фильмов».
Инструменты
Нам потребуется:
Взгляд на backbone.js
Задача данного фреймворка не в том чтобы дать вам кучу виджетов, и даже не в том, чтобы обеспечить уровень представления (view). Его задача дать вам несколько ключевых объектов, которые помогут структурировать код.
Нам понадобятся объекты Model, Collection, View и Controller.
Модель
Для получения полнофункциональной модели достаточно написать всего одну строчку кода:
var Movie = Backbone.Model.extend({});
Теперь мы можем получить экземпляры объекта, задавать и получать произвольные атрибуты:
matrix = new Movie(); matrix.set({ title: "The Matrix", format: "dvd' }); matrix.get('title');
Также можно передавать атрибуты напрямую в конструктор при создании объекта:
matrix = new Movie({ title: "The Matrix", format: "dvd' });
Выполнить какие-то проверки или иные действия при создании объекта, можно расширив модель функцией
initialize(). При создании объекта она будет вызвана с параметром, который вы передали в конструктор.
var Movie = Backbone.Model.extend({ initialize: function (spec) { if (!spec || !spec.title || !spec.format) { throw "InvalidConstructArgs"; } } });
Также можно определить метод validate(), он будет вызываться каждый раз, когда вы задаете атрибуты и используется для валидации атрибутов. В случае если этот метод что-либо возвращает, атрибут не устанавливается:
var Movie = Backbone.Model.extend({ validate: function (attrs) { if (attrs.title) { if (!_.isString(attrs.title) || attrs.title.length === 0 ) { return "Название должно быть непустой строкой"; } } } });
Для более полного ознакомления с возможностями backbone предлагаю ознакомиться с документацией.
Коллекции
Коллекция в backbone представляет из себя упорядоченный список моделей некоторого типа. В отличие от обычного массива, коллекции обеспечивают гораздо больше функционала, такого как, например, установка правил сортировки с помощью метода comparator().
После того как определен тип модели в коллекции, добавление туда объекта выглядит чрезвычайно просто:
var MovieList = Backbone.Collection.extend({ model: Movie }); var library = new MovieList(); library.add({ title: "The Big Lebowski", format: "VHS" });
Представления
В общих чертах, представления backbone определяют правила отображения изменений модели в браузере.
Здесь начинаются манипуляции с DOM и в игру вступает jQuery. Для изначальной загрузки моделей в DOM нам потребуется шаблонизатор, мы воспользуемся связкой ICanHaz.js + mustache.js
Вот пример представления для нашего приложения:
var MovieView = Backbone.View.extend({ render: function() { var context = _.extend(this.model.toJSON(), {cid: this.model.cid}); this.el = ich.movie(context); return this; } });
Соберем все вместе
До сих пор мы говорили о разных частях приложения, теперь посмотрим как объединить их в одно целое.
Контроллер
В контроллере мы свяжем все части приложения, а также определим пути для манипуляций с объектами и связанные с ними методы.
var MovieAppController = Backbone.Controller.extend({ initialize: function(params) { this.model = new MovieAppModel(); this.view = new MovieAppView({ model: this.model }); params.append_at.append(this.view.render().el); }, routes: { "movie/add": "add", "movie/remove/:number": "remove", }, add: function() { app.model.movies.add(new Movie({ title: 'The Martix' + Math.floor(Math.random()*11), format: 'dvd' })); // сбросим путь чтобы метод можно было вызвать еще раз this.saveLocation(); }, remove: function(cid) { app.model.movies.remove(app.model.movies.getByCid(cid)); this.saveLocation(); } });
Здесь мы видим, что в контроллере сохраняется модель приложения, которая будет хранить все остальные модели и коллекции, а также представление приложения.
Модель приложения в нашем случае будет хранить коллекцию фильмов:
var MovieAppModel = Backbone.Model.extend({ initialize: function() { this.movies = new MovieList(); } });
Представление приложения будет выглядеть так:
var MovieAppView = Backbone.View.extend({ initialize: function() { _.bindAll(this, "addMovie", "removeMovie"); this.model.movies.bind('add', this.addMovie); this.model.movies.bind('remove', this.removeMovie); }, render: function() { $(this.el).html(ich.app(this.model.toJSON())); this.movieList = this.$('#movieList'); return this; }, addMovie: function(movie) { var view = new MovieView({model: movie}); this.movieList.append(view.render().el); }, removeMovie: function(movie) { this.$('#movie_' + movie.cid).remove(); } });
Ну и собственно индексный файл со всеми зависимостями и шаблонами:
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Movie App</title> <!-- libs --> <script src="js/lib/jquery.js"></script> <script src="js/lib/underscore.js"></script> <script src="js/lib/backbone.js"></script> <!-- templating --> <script src="js/lib/mustache.js"></script> <script src="js/lib/ICanHaz.js"></script> <!-- app --> <script src="js/app/Movie.js"></script> <script src="js/app/MovieCollection.js"></script> <script src="js/app/MovieView.js"></script> <script src="js/app/MovieAppModel.js"></script> <script src="js/app/MovieAppView.js"></script> <script src="js/app/MovieAppController.js"></script> <script type="text/javascript"> $(function() { var movieApp = new MovieAppController({append_at: $('body')}); window.app = movieApp; Backbone.history.start(); }); </script> <!-- ich templates --> <script id="app" type="text/html"> <h1>Movie App</h1> <a href="#movie/add">add new movie</a> <ul id="movieList"></ul> </script> <script id="movie" type="text/html"> <li id="movie_{{ cid }}"><span class="title">{{ title }}</span> <span>{{ format }}</span> <a href="#movie/remove/{{ cid }}">x</a></li> </script> </head> <body> </body> </html>
Все приложение готово. Конечно, это только очень малая часть тех возможностей которые предоставляют данные библиотеки, но думаю, что данного примера достаточно, что почувствовать вкус к разработке, с помощью этих инструментов.
