Модель представляет собой данные. Она однозначно идентифицируется своим ключом, который строится во время инициализации. Разный ключ всегда означает разный экземпляр модели.
- Декларация
- Получение экземпляра модели
- Запрос данных модели с сервера
- Работа с данными
- Постобработка данных
- События
- Запрос
Определение новой модели происходит через статическую функцию 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-changedns-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);
}
}
});