Backbone.js предоставляет минималистичный каркас для построения клиентских приложений, и одной из его ключевых возможностей является работа с моделями и коллекциями. Однако при создании сложных приложений возникает необходимость в установлении связей между моделями, что реализуется через model relationships и associations. Это позволяет организовать данные в виде структуры с вложенными объектами и связями «один ко многим» или «многие ко многим».
В Backbone.js модель (Backbone.Model) представляет собой
объект с набором атрибутов и методами для работы с ними:
var Person = Backbone.Model.extend({
defaults: {
name: '',
age: 0
}
});
Коллекция (Backbone.Collection) — это упорядоченный
набор моделей одного типа:
var People = Backbone.Collection.extend({
model: Person
});
Коллекции автоматически отслеживают изменения своих моделей, а модели уведомляют коллекции о событиях изменения.
Backbone.js не предоставляет встроенной системы ассоциаций, как, например, ActiveRecord в Rails, но концепцию связей можно реализовать через вложенные модели и коллекции.
Пример связи “один ко многим”:
var Task = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
var TaskList = Backbone.Collection.extend({
model: Task
});
var Project = Backbone.Model.extend({
defaults: function() {
return {
name: '',
tasks: new TaskList()
};
},
initialize: function() {
this.get('tasks').parentProject = this;
}
});
В данном примере Project содержит коллекцию
tasks. Каждая задача принадлежит конкретному проекту. При
инициализации проекта коллекция задач получает ссылку на родительский
объект, что позволяет легко обращаться к связанной модели.
Для загрузки данных с сервера, содержащих вложенные объекты, часто используют parse-функцию модели. Она позволяет корректно преобразовывать JSON-данные в вложенные модели и коллекции.
var Project = Backbone.Model.extend({
defaults: function() {
return {
name: '',
tasks: new TaskList()
};
},
parse: function(response) {
if (response.tasks) {
this.get('tasks').reset(response.tasks);
delete response.tasks;
}
return response;
}
});
var project = new Project();
project.set(project.parse({
name: 'Website Redesign',
tasks: [
{title: 'Design mockups', completed: true},
{title: 'Implement frontend', completed: false}
]
}));
Метод parse позволяет отделить вложенные коллекции от
основной модели, обеспечивая правильную инициализацию всех зависимых
объектов.
Backbone.js строит реактивную архитектуру на событиях. Связи между моделями можно реализовать через подписку на события изменения атрибутов:
var Task = Backbone.Model.extend({});
var Project = Backbone.Model.extend({
defaults: function() {
return {
tasks: new TaskList()
};
},
initialize: function() {
this.listenTo(this.get('tasks'), 'add remove change', this.onTasksChange);
},
onTasksChange: function(task) {
console.log('Tasks updated:', task.toJSON());
}
});
Каждое добавление, удаление или изменение задачи автоматически вызывает обработчик в родительской модели, что позволяет синхронизировать состояние модели и связанных коллекций.
Для реализации отношений «многие ко многим» создается промежуточная коллекция, которая хранит ссылки на обе стороны связи. Например, пользователи и группы:
var User = Backbone.Model.extend({});
var Group = Backbone.Model.extend({});
var Membership = Backbone.Model.extend({
defaults: {
user: null,
group: null
}
});
var Memberships = Backbone.Collection.extend({
model: Membership
});
// Пример создания связи
var user = new User({name: 'Alice'});
var group = new Group({name: 'Admins'});
var memberships = new Memberships();
memberships.add(new Membership({user: user, group: group}));
Такой подход позволяет поддерживать гибкую структуру данных и легко реализовывать фильтрацию, выборку и обновление связей между моделями.
defaults
или initialize. Это гарантирует, что коллекции
всегда доступны и корректно связаны с родительской моделью.parse при загрузке вложенных
данных. Он позволяет корректно преобразовать серверный JSON в
Backbone-объекты.Для удобства работы с ассоциациями часто создают вспомогательные методы, позволяющие получить связанные объекты или добавить новые:
var Project = Backbone.Model.extend({
defaults: function() {
return {tasks: new TaskList()};
},
addTask: function(attrs) {
return this.get('tasks').add(new Task(attrs));
},
removeTask: function(task) {
this.get('tasks').remove(task);
},
getCompletedTasks: function() {
return this.get('tasks').filter(task => task.get('completed'));
}
});
Методы инкапсулируют работу с вложенными коллекциями, обеспечивая чистый и понятный интерфейс для управления связями между моделями.