Данная статья является вольным переводом. Оригинал тут.
Думаю, ни для кого не секрет, что клиентские приложения в современных веб-сервисах становится все сложнее и количество 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>
Все приложение готово. Конечно, это только очень малая часть тех возможностей которые предоставляют данные библиотеки, но думаю, что данного примера достаточно, что почувствовать вкус к разработке, с помощью этих инструментов.