Skip to content

bem/bem-mvc

Repository files navigation

Yet another MVC for i-bem

Набор i-bem блоков для реализации MVC-паттерна. Предоставляет API для работы с моделями и блоки для автоматического провязывания моделей с интерфейсом.

Требования к моделям

  • Декларативный стиль описания моделей
  • Доступ к созданным экземплярам моделей по имени и id
  • Автоматическое приведение значений полей к заданному типу
  • Валидация моделей

Требование к биндингам

  • i-bem ориентированность
  • Наследование собственной функциональности в использующих i-bem блоках
  • Провязка с контролами из bem-controls

Зависимости

Модели

Для использования модели необходимо задекларировать её, указав имя модели и описав поля.

BEM.MODEL.decl('model', {
    name: 'string',
    birth: { 
        type: 'string',
        preprocess: function(value) {
            return value.year + '.' + value.month + '.' + value.day;
        }
    },
    height: 'number',
    weight: 'number'
});

Также при декларации можно указать методы модели (переопределение базовых методов модели породит ошибку).

BEM.MODEL.decl('model', {
    name: 'string',
    hasBoyfriend: {
        type: 'boolean',
        default: false
    }
}, {
    toggleStatus: function() {
        this.set('hasBoyfriend', !this.get('hasBoyfriend'));

        return this;
    }
});

var model = BEM.MODEL.create('model', { name: 'Claudia Schiffer', hasBoyfriend: true });
model.toggleStatus();
model.get('hasBoyfriend'); // false

Типы полей

  • string – строка
  • number – число
  • boolean – булеан
  • model – модель
  • array – массив произвольных данных
  • models-list – список моделей одного типа

Чтобы создать модель, нужно указать имя модели и, если нужно, передать инициализационные параметры

var model = BEM.MODEL.create('model', {
    name: 'Claudia Schiffer',
    birth: {
        year: 1970,
        month: 8,
        day: 25
    },
    weight: 75,
    height: 180.5
});

В случае, когда отсутствует ссылка на экземпляр модели, ее можно запросить из хранилища, например, по имени

var model = BEM.MODEL.get('model')[0];

Теперь можно устанавливать поля модели

model
    .set('weight', '80') // будет приведено к number
    .set('height', 180)
    .update({
        weight: 80,
        height: 180
    });

И получать их значения

model.get('weight'); // 80
model.get('birth'); // '1970.8.25'

model.toJSON(); // все поля модели

О изменениях можно узнавать с помощью событий

model.on('weight', 'change', function() {
    alert('Пора худеть!');
});

Чтобы валидировать модель, нужно задать правила валидации

BEM.MODEL.decl('model-with-validation', {
    name: 'string',
    birth: { 
        type: 'string',
        preprocess: function(value) {
            return value.year + '.' + value.month + '.' + value.day;
        }
    },
    height: { 
        type: 'number',
        validation: {             // задать функцию валидации
            validate: function(value) {
                return value >= 170; 
            }
        }
    },
    weight: {
        type: 'number',
        validation: {
            rules: {               // или правила валидации:
                required: true,      // стандартное
                toFat: {             // и кастомное
                    needToValidate: function() {        // проверить нужно ли выполнять валидацию
                        return this.get('height') > 170;
                    },
                    validate: function(value) {
                        return value <= 90;
                    }
                }
            }
        }
    }
});

И проверить на валидность

model.isValid();

Биндинги

Для провязывания модели с DOM-представлением используется блок i-glue. Блок которой "проклеивает" модель и DOM. Для того, чтобы провязать модель с каким либо контролом, необходимо на родительский блок примешать блок i-glue, а на контрол элемент model-field блока i-glue и указать им параметры модели. BEMJSON в таком случае будет выглядеть так:

{
    block: 'b-model',
    mix: [{
        block: 'i-glue',                   // примешиваем блок i-glue
        js: {
            modelName: 'model',            // указываем имя модели
            modelData: {
                name: 'Claudia Schiffer',  // и данные
                weight: 75,
                height: 180.5
            }
        }
    }],
    content: [
        ...

        {
            block: 'input', 
            mix: [{                        // на поле ввода примешиваем элемент model-field
                block: 'i-glue', 
                elem: 'model-field',
                js: {
                    name: 'weight',         // указываем ему с каким полем провязываться
                    type: 'input'
                }
            }],
            name: 'weight',
            value: '75', 
            mods: { size: 's' },
            content: { elem: 'control' }
        }
        
        ...
    ]
}

После инициализации будет создана модель, с указанными данным. И изменения в поле ввода, будут автоматически отражаться в модели (и наоборот).

Типы биндингов (модификаторы model-field)

  • input – провязка с блоком input
  • select – провязка с блоком select
  • checkbox – провязка с блоком checkbox
  • inline – вставка значения поля в html
  • mod – изменение модификатора блока

Агрегация моделей

Иногда создание моделей с помощью блока i-glue может быть неудобной. Для случая, когда данные для модели генерируются во время шаблонизации, можно использовать блок i-model.

{
    block: 'i-model',
    modelName: 'super-model',
    modelData: {
        name: 'Claudia Schiffer',
        weight: 75,
        height: 180.5
    }
}
// или
{
    block: 'i-model',
    modelParams: {
        name: 'super-model',
        data: {
            name: 'Claudia Schiffer',
            weight: 75,
            height: 180.5
        }
    }
}

В таком случае в DOM'е появится столько элементов, сколько в конечном bemjson'е блоков i-model. Чтобы избежать засорения DOM'а вспомогательными объектам, можно любой контент обернуть в блок i-model-aggregator

{
    block: 'i-model-aggregator',
    content: [
        { block: 'i-model', modelName: 'model1' },
        {
            block: 'view-block',
            content: [
                { block: 'i-model', modelName: 'model2' }
            ]
        },
        { block: 'i-model', modelName: 'model3' }
    ]
}

В итоге все блоки i-model внутри агрегатора будут объединены в один и модели будут проинициализированы до инициализации других блоков.

Unit-тестирование

В консоли

Тесты располагаются в директории tests/specs согласно файловой структуре.

Запуск npm test

В браузере

Тесты располагаются в файлах *.test.js.

Запуск bem server

Открыть страницу localhost:8080/desktop.bundles/tests/tests.html

Lint

Запуск npm run lint

Ссылки

JS Docs: