Модель представляет собой данные. Она однозначно идентифицируется своим ключом, который строится во время инициализации. Разный ключ всегда означает разный экземпляр модели.
- Декларация
- Получение экземпляра модели
- Запрос данных модели с сервера
- Работа с данными
- Постобработка данных
- События
- Запрос
Определение новой модели происходит через статическую функцию ns.Model.define
ns.Model.define('modelName', modelDeclObject[, baseModel])
Объект-декларация состоит из следующих свойств.
ctor
- это функция-конструтор. Обратите внимание, что он вызывается самым первым, до инициализации самой модели, т.о. в конструкторе еще не доступны некоторые свойства.
Полностью готовый экземпляр бросает событие ns-model-init
.
/**
* @classdesc prj.mMyModel
* @augments ns.Model
*/
ns.Model.define('my-model', {
/**
* @constructs prj.mMyModel
*/
ctor: function() {
this._state = 'initial';
this.CONST = 100;
}
});
events
- объект с декларацией подписок на события noscript.
Любая подписка имеет вид:
{
"на что подписаться": "обработчик"
}
Обработчиком может быть название метода из прототипа или функция.
Пример:
{
"my-custom-event": "onCustomEvent",
"my-custom-show@show": "onCustomShow"
}
methods
- объект с методами. По сути является прототипом объекта.
/**
* @classdesc prj.mMyModel
* @augments ns.Model
*/
ns.Model.define('my-model', {
/** @lends prj.mMyModel.prototype */
methods: {
BAR: 100
foo: function(){}
}
});
Параметры нужны для как для построения ключа, так и для запроса моделей с сервера.
ns.Model.define('my-model', {
params: {
// Любое значение, кроме null расценивается как дефолтное значение этого параметра.
'author-login': null,
'album-id': null,
// Этим двум параметрам заданы дефолтные значения.
'page': 0,
'pageSize': 20
}
});
В запросе на сервер отправляются все параметры, которые не null
.
Важно понимать, что HTTP - текстовый протокол, поэтому все значения отправятся как строки.
Т.о. 0
станет "0"
, false
- "false"
. А это значит, что параметры, которые в вашем приложении не обрабатываются как строки, надо приводить к правильному типу на сервере. Иначе, можно получить такую ошибку
// отправили параметры как
// ?lag=false
//
if (params.flag) {
// эта ветка выполнится, потому что params.flag === "false"
}
ns.Model.get('modelName', params)
- строит ключ изparams
и возвращает соответствующую модель. Если такого экземпляра нет, то он будет создан.ns.Model.getValid('modelName', params)
- тоже самое что иns.Model.get
. Только экземпляр еще проверяется на валидность. Если валидный экземпляр не найден, то возвращаетсяnull
.
Данные модели:
- могут прийти с севера (модель можно явно запросить с помощью метода
ns.request()
или неявно, создав и выполнивns.Update
на странице) - могут быть установлены вручную (см. Работа с данными)
В случае запроса модели с сервера модель перезапрашивается если:
- она не валидна (
isValid()
возвращаетfalse
) - её можно запросить ещё раз (
canRequest()
возвращаетtrue
)
Локальная модель - модель, которая никогда не запрашивается на сервере.
Данные такой модели устанавливают вручную.
Чтобы модель стала локальной нужно переопределить метод canRequest()
, к примеру, так:
ns.Model.define('local-model', {
methods: {
canRequest: function() {
return false;
}
}
});
Методы для получения данных:
#getData()
- возвращает весь объект данных модели. Этот метод можно переопределять для доп. обработки данных. Например, для коллекции этот метод собирает актуальные данных из всех элементов.#get(jpath)
- выбирает данные по jpath и приводит результат к упрощенному виду. Результат приведения зависит как от самих данных, так и от jpath. Поэтому при изменениях формат результата может меняться.
{
"foo": "1",
"bar": [
{ "id": 1 }
]
}
this.get('.foo') -> "1"
this.get('.bar.id') -> ["1"]
#select(jpath)
- выбирает данные по jpath. В отличии от#get
, не занимается приведением и всегда возвращает массив результатов выборки, т.о. формат результат остается стабильным при изменениях.
{
"foo": "1",
"bar": [
{ "id": 1 }
]
}
this.get('.foo') -> ["1"]
this.get('.bar.id') -> ["1"]
Методы для изменения данных:
#set(jpath, value)
- изменяет данные по jpath. Поддерживаются только несложные jpath.
this.set('.foo', 2);
#setData(data)
- устаналивает полностью новые данные. В частности, этот метод вызывается при получении данных с сервера.
Метод извлекает данные из ответа сервера. По умолчанию берется поле data
из ответа. Если метод не возвращает данные, то считается, что модель загружена с ошибкой.
ns.Model.define('my-model', {
methods: {
extractData: function(serverResponse) {
if (serverResponse) {
return serverResponse.result;
}
}
}
});
Метода извлекает данные об ошибке сервера. По умолчанию берется поле error
из ответа.
Метод вызывается, когда #extractData()
не вернул данные.
ns.Model.define('my-model', {
methods: {
extractError: function(serverResponse) {
if (serverResponse) {
return serverResponse.error;
}
}
}
});
Этот метод может контроллировать изменились ли данные на самом деле, чтобы не вызывать лишних события и перерисовок.
Аргументом метода являются новые данные, а старые можно получить способами описанными выше, например #getData
. Должен вернуть boolean
.
ns.Model.define('my-model', {
methods: {
hasDataChanged: function(newData) {
var oldData = this.getData;
// изменяем данные, только если изменилось поле id
return oldData.id !== newData.id
}
}
});
Этот метод позволяет обработать полученные данные. Аргументом метода являются новые данные, должен вернуть обработанные данные.
ns.Model.define('my-model', {
methods: {
_index: null,
preprocessData: function(newData) {
var that = this;
// строим индекс для быстрого поиска
newData.forEach(function(item) {
that._index[item.id] = item;
});
return newData;
}
}
});
ns-model-changed
- модель изменилась. В аргументах приходит jpath, по которому было сделано изменение. Если он пустой, то изменилась вся модель (обычно методом#setData()
)ns-model-changed<.jpath>
- изменились данные по указанному jpath. В аргументах приходит jpath, по которому было сделано изменение. События кидаются иерархично, т.о. для.for.bar
будет три события:ns-model-changed.foo.bar
,ns-model-changed.foo
,ns-model-changed
ns-model-destroyed
- модель была инвалидированна и уничтожена.ns-model-init
- модель создана и проинициализованнаns-model-touched
- у модели изменилась версия. Такое событие будет как результатом изменения данных через#set
или#setData
, так и прямым вызовом метода#touch()
По умолчанию все модели запрашиваются по урлу ns.request.URL
.
Если запрашиваются несколько моделей, то они группируются в один запрос.
Это поведение можно изменить с помощью метода request()
у модели, который должен вернуть Vow.Promise
.
В этом случае, вся логика запроса находится в этом методе, в том числе модель сама должна вызвать методы setData()
или setError()
.
Такие запросы не группируются, но подчиняются общим правилам ns.request
, т.е. будут работать перезапросы и пока не завершится первый запрос, нельзя сделать дублирующий.
ns.Model.define('model', {
methods: {
request: function() {
return ns.http('https://api.twitter.com', {}, {type: 'GET'})
.then(function(data) {
this.setData(data);
}, function(error) {
this.setError(error);
}, this);
}
}
});