Backbone.Associations

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


Основные понятия

Модель с ассоциациями — это Backbone.Model, расширенная функциональностью для работы с зависимыми моделями или коллекциями. Каждая ассоциация описывается в виде конфигурации в объекте associations при определении модели.

Типы ассоциаций:

  1. One-to-One (один к одному) Используется для вложенных моделей. Например, у пользователя может быть один профиль:

    var Profile = Backbone.AssociatedModel.extend({
        defaults: {
            bio: '',
            avatar: ''
        }
    });
    
    var User = Backbone.AssociatedModel.extend({
        associations: [
            {
                type: Backbone.One,
                key: 'profile',
                relatedModel: Profile
            }
        ]
    });
    
    var user = new User({
        profile: {
            bio: 'Frontend разработчик',
            avatar: 'avatar.png'
        }
    });
    console.log(user.get('profile').get('bio')); // "Frontend разработчик"

    Ключевой момент: при присвоении вложенного объекта автоматически создается экземпляр связанной модели.

  2. One-to-Many (один ко многим) Подходит для случаев, когда одна модель владеет коллекцией других моделей:

    var Comment = Backbone.AssociatedModel.extend({
        defaults: {
            text: ''
        }
    });
    
    var Post = Backbone.AssociatedModel.extend({
        associations: [
            {
                type: Backbone.Many,
                key: 'comments',
                relatedModel: Comment,
                collectionType: Backbone.Collection
            }
        ]
    });
    
    var post = new Post({
        comments: [
            { text: 'Отличная статья!' },
            { text: 'Полезно' }
        ]
    });
    
    console.log(post.get('comments').at(0).get('text')); // "Отличная статья!"

    Важно: Backbone.Associations автоматически превращает массив объектов в коллекцию экземпляров Comment.


Вложенные ассоциации

Ассоциации могут быть многоуровневыми. Например, пост может содержать комментарии, а каждый комментарий — автора:

var Author = Backbone.AssociatedModel.extend({
    defaults: {
        name: ''
    }
});

var Comment = Backbone.AssociatedModel.extend({
    associations: [
        {
            type: Backbone.One,
            key: 'author',
            relatedModel: Author
        }
    ]
});

var Post = Backbone.AssociatedModel.extend({
    associations: [
        {
            type: Backbone.Many,
            key: 'comments',
            relatedModel: Comment,
            collectionType: Backbone.Collection
        }
    ]
});

var post = new Post({
    comments: [
        { text: 'Отлично', author: { name: 'Иван' } },
        { text: 'Супер', author: { name: 'Мария' } }
    ]
});

console.log(post.get('comments').at(0).get('author').get('name')); // "Иван"

Такой подход позволяет строить сложные вложенные структуры данных с автоматическим созданием и синхронизацией всех зависимых моделей.


Работа с коллекциями

Backbone.Associations расширяет стандартные коллекции Backbone возможностью реагировать на изменения вложенных моделей. Коллекции автоматически обновляются при добавлении, удалении или изменении связанных объектов.

var Task = Backbone.AssociatedModel.extend({
    defaults: { title: '', done: false }
});

var Project = Backbone.AssociatedModel.extend({
    associations: [
        {
            type: Backbone.Many,
            key: 'tasks',
            relatedModel: Task
        }
    ]
});

var project = new Project({
    tasks: [{ title: 'Сделать тесты' }, { title: 'Написать документацию' }]
});

project.get('tasks').add({ title: 'Подготовить релиз' });
console.log(project.get('tasks').length); // 3

Коллекции могут использовать стандартные методы Backbone (add, remove, at, each) с полной поддержкой ассоциаций.


События и синхронизация

Ассоциации полностью интегрированы с системой событий Backbone. Любое изменение вложенной модели или коллекции вызывает соответствующие события на родительской модели.

  • change:<key> — событие изменения конкретной ассоциации.
  • add, remove, reset — события коллекции.
var Post = Backbone.AssociatedModel.extend({
    associations: [
        {
            type: Backbone.Many,
            key: 'comments',
            relatedModel: Comment
        }
    ]
});

var post = new Post({ comments: [] });

post.on('change:comments', function() {
    console.log('Комментарии изменились');
});

post.get('comments').add({ text: 'Новый комментарий' }); // "Комментарии изменились"

Эта функциональность позволяет отслеживать изменения на всех уровнях вложенности без необходимости вручную управлять слушателями на каждой вложенной модели.


Настройка сериализации

Backbone.Associations добавляет возможность сериализации моделей с ассоциациями с помощью метода toJSON(). По умолчанию вложенные модели и коллекции автоматически конвертируются в JSON:

var post = new Post({
    comments: [
        { text: 'Первый' },
        { text: 'Второй' }
    ]
});

console.log(JSON.stringify(post.toJSON(), null, 2));
/*
{
  "comments": [
    { "text": "Первый" },
    { "text": "Второй" }
  ]
}
*/

Также можно настроить сериализацию, исключая определённые поля или преобразуя данные перед отправкой на сервер.


Плюсы использования Backbone.Associations

  • Автоматическое создание вложенных моделей и коллекций.
  • Поддержка событий на всех уровнях вложенности.
  • Простая интеграция с серверными API.
  • Упрощение работы с JSON-структурами сложной вложенности.

Backbone.Associations является мощным инструментом для построения сложных клиентских моделей, где требуется строгая структура данных и управление зависимостями между объектами.