Предисловие


Цель данного поста — популяризация веб-фреймворка Jiant и поиск людей, которые хотели бы принять участие в его раскрутке, создание сообщества для дальнейшего развития и распространения фреймворка. Jiant активно использует JQuery и естественно с ним взаимодействует, выполняя свою цель по организации и упрощению веб приложений любой сложности.

Целевая аудитория — полагаю, будет полезно любому веб-разработчику, но особенно тем кто пришел с сервера — например, из джавы.

Ссылка на github: https://github.com/vecnas/jiant
Ссылка на todo-mvc: https://github.com/vecnas/todo-mvc

Введение


Jiant — фреймворк для разработки веб-приложений, предоставляющий набор инструментов, упрощающих и ускоряющих разработку и затем поддержку JavaScript приложений.

Jiant приложение объявляется как переменная JavaScript и содержит несколько секций. Одна из таких секций — models. В этой секции объявляются модели данных приложения:

var app = {
  models: {
    user: {
      //declaration is here
    },
    env: {
      //declaration is here
    }
  }
}

Для каждой модели описываются ее поля в минимальной форме, затем Jiant автоматически реализует модель. Модели бывают двух типов — singleton(единственный объект данных в приложении) и репозиторий (коллекция объектов).

Преимущества перед всеми известными мне на текущий момент фреймворками — минимум кода, удобство автозавершения в IDE, автодокументирование, простота понимания, естественная декомпозиция приложения на простейшие части.

Singleton модель


Объявление включает список полей и несколько функций, добавляемых по умолчанию, таких как update, on, off:

models: {

  loggedUser: {
    // auto-added function, declared for readability
    update: function(obj) {},

    // model fields
    firstName: function(val) {},
    lastName: function(val) {},
    id: function(val) {}
  }

}

Или так:

  app.models.loggedUser = {
    // auto-added function, declared for readability
    update: function(obj) {},

    // model fields
    firstName: function(val) {},
    lastName: function(val) {},
    id: function(val) {}
  }

Полями считаются все пустые функции кроме нескольких предопределенных, таких как update, on. Реализация выполняется Jiant'ом в момент вызова следующей функции, в этот момент все модели должны быть добавлены в файл описания приложения:

jiant.bindUi(app);

Изменение свойств модели

После связывания можно пользоваться моделью, как правило внутри метода jiant.onUiBound:

jiant.onUiBound(app, function($, app) {
  var loggedUser = app.models.loggedUser;
  loggedUser.firstName("Joy");
})

Если есть данные в виде json, можно просто вызвать update (наиболее часто используемый сценарий — получение данных с сервера):

var userData = {
  firstName: "John",
  lastName: "Smith",
  id: 1234
};
app.models.loggedUser.update(userData);

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

var loggedUserId = app.models.loggedUser.id();

Можно заметить, что сеттер и геттер имеют одинаковый синтаксис, модель сама определяет что сейчас вызывается:

loggedUser.firstName() // getter
loggedUser.firstName("Johann") // setter
loggedUser.firstName(undefined) // setter

Синтетические методы

В рамках модели можем определить методы, предоставляющие более сложные функции чем setter/getter:

loggedUser: {
  //...
  fullName: function() { 
              return this.firstName() + " " + this.lastName()
            }
}

Можно вернуть ссылку на другую модель или просто сообщить залогинен ли кто-нибудь:

models: {
  env: {
    loggedUser: function() {return app.models.loggedUser}
    isLogged: function() {return !!app.models.loggedUser.id()}
  }
}

На синтетических методах не генерируются уведомления об изменении (нет методов on, off, asap).

Подписка на изменения

Каждое свойство модели предоставляет метод .on(callback), для подписки на изменение данного свойства:

jiant.onUiBound(app, function($, app) {
  app.models.loggedUser.id.on(function(loggedUser, id) {
    alert("logged user id changed: " + id);
  }
})

Полный синтаксис callback для метода on:

function(modelObject, newValue, oldValue)

Метод off идет в комплекте. Также для самого модельного объекта доступна подписка на любые изменения его полей через все тот же метод on:

  app.models.loggedUser.on(function(loggedUser) {})

ASAP

Нередки ситуации когда необходимо выполнить какой-то код когда доступны определенные данные, либо немедленно если они уже доступны. В этом случае используется метод asap (as soon as possible), который срабатывает только один раз — либо сразу, либо когда появляется требуемое значение:

app.models.loggedUser.id.asap(function(loggedUser, id) {
  alert("Hello " + loggedUser.fullName());
})

Репозиторий-модель


Основное отличие репозитория в том, что в нем хранится много объектов данной модели, а не одна. Соответственно, работа с репозиторием осуществляется как с коллекцией. Название репозиторий используется так как изначально идея была рождена на базе JpaRepository из Java Spring фреймворка. При этом синтаксис объявления похож на синглтон, но добавляются методы работы с коллекцией:

models: {
  listing: {
    updateAll: function(arr, removeMissing) {},
    add: function(arr) {},
    remove: function(obj) {},
    
    all: function() {},
    findById: function(val) {},
    listByTp: function(val) {},

    id: function(val) {},
    tp: function(val) {},
    price: function(val) {},
    baths: function(val) {},

    ui: function(val) {}
  }
}

Модификация репозитория

Методы add, remove делают в точности то что говорит их название. Единственный не очевидный метод — updateAll — он производит сравнение по полю id массива или объекта который ему передан и текущего содержимого репозитория, после чего добавляет-удаляет-обновляет содержимое. В случае если у объекта нет идентификатора — можно либо объявить синтетический метод id(), либо передать 3й параметр методу updateAll, функцию которая сравнит два объекта и вернет true/false.

Метод remove можно использовать любым способом, допустим если есть объект данной коллекции obj, тогда обе следующие строки делают одно и то же:

app.models.listing.remove(obj);
obj.remove()

Метод update в случае репозитория применяется к отдельному объекту, а не ко всему репозиторию в целом:

  var obj = app.models.listing.findById(365);
  obj.update(newJsonData);
  // или
  obj.price(newPrice);

Получение объектов репозитория

Метод all возвращает все объекты репозитория и доступен всегда. Также можно определить методы поиска findByXXX, listByXXX, которые ищут в репозитории по значению поля (или нескольких):

  findByTp: function(val) {} // ищем по полю tp
  listByTpAndBaths: function(tp, baths) {} // ищем сразу по двум полям

Перечень полей если их больше одного — разделяется словом And. Реализация этих методов осуществляется автоматически. Допустим, понадобилось искать еще по какому-то полю — добавляем пустую функцию и готово.

Различие find и list в том что find возвращает только один объект (первый попавшийся, либо нулл), а list всегда возвращает массив (со списком объектов или пустой).

Функции коллекций

Методы all и listBy всегда возвращают массивы, но массивы не простые, а обогащенные методами базовой модели, такими как геттеры, update, remove. Например:

app.models.listing.listByTp("obsolete").remove() // удаляем все tp=="obsolete" листинги
app.models.listing.all().price("n/a") // ставим всем цену n/a
app.models.listing.all().ui() // массив ui объектов
app.models.listing.all().asMap() // содержимое репозитория в виде json

Не только данные

В модели можно хранить не только данные, а любые объекты, например в примере выше есть поле ui — в нем мы будем хранить визуальное представление листинга на интерфейсе, ссылку на jQuery объект и сможем доступаться к нему в любой момент в нужном месте.

app.models.listing.findById(234).ui().addClass("active")

Примеры


Вот некоторые примеры использования вышеперечисленных функций:

asap — в проекте использовались google maps, и выставлять на них маркеры нужно было когда они проинициализированы. Чтобы не пихать весь код в одно место — добавлено поле gmap в модель env и код расставляющий маркеры начинал работать когда оно выставлялось. Получились два небольших независимых участка кода вместо одного месива.

singleton — постоянно используется для работы с окружением (тот же пример с google maps, аналогии легко добавить) или информации о залогиненном пользователе

repository — любые загружаемые с сервера списки данных, отображаемые на интерфейсе или используемые в программной логике. Например — список штатов, с возможностью поиска по коду и потом отображение на интерфейсе полного имени:

models: {
  state: {
    updateAll: function(arr) {},

    findByShort: function(val) {},

    short: function(val) {},
    full: function(val) {}
  }
}

И затем:

var fullStateName = app.models.state.findByShort(short).full()

Другой пример — уведомления и нотификации. Поступающие уведомления просто скидываются в модель. Отдельно существующий независимый код висит над этой моделью и показывает уведомления пользователю, удаляя те что пользователь убрал.

Еще меньше кода

Объявив

var f = function(val) {}

можно уменьшить количество кода:

models: {
  state: {
    updateAll: function(arr) {},

    findByShort: f,

    short: f,
    full: f
  }
}

Не делайте так, не будет подсказки от IDE что это функция (пока не разработают плагин).

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

Бонусы


Документация за просто так. Объявив модель, сразу получаем документацию к приложению.
Ускорение разработки. Меньше писать.
Значительное упрощение поддержки. Приложение разбито на произвольно мелкие части. IDE автозавершение и поиск «мест использования» работают, что крайне редко для javascript кода.
Главный недостаток — излишняя простота — было такое что поначалу непонятно «как это может работать, тут же просто пустая функция». Риторический вопрос, недостаток ли это.

Комментарии (24)


  1. affka
    11.11.2015 14:20

    Интересно… а после методов findById, all — тоже работает автозаполнение?
    Есть ли какие-то примеры приложений на вашем фреймворке?


    1. Vecnas
      11.11.2015 20:01

      добавил ссылку на todo-mvc в начало статьи


  1. kahi4
    11.11.2015 15:02
    +1

    Эмм. Бэкбон без вьюшек и кривоватым API?


    1. Vecnas
      11.11.2015 15:09

      почему без вьюшек, если охватить все, то сейчас там есть вьюшки, шаблоны, event bus, абстракция программной логики (а-ля джавовские интерфейсы с подменяемой реализацией), аякс абстракции, модули, API для работы с хэш-навигацией (на порядок лучше чем любая из известных мне реализаций, включая убогий angular), возможность загрузки сразу нескольких приложений на страницу, с разделяемой опять же хэш-навигацией, кроссдоменность на лету, подгрузка html-реализации тех же вьюшек и шаблонов. Просто это очень много всего, здесь я написал только про один из компонентов.
      Кстати, чем API кривовато?


      1. kahi4
        11.11.2015 15:23

        Кстати, чем API кривовато?

        Как минимум, тем, что одна модель и их коллекция одинаковы в объявлении. Это прям таки антипаттерн («Золушкина туфелька», как подсказывает википедия), но, как минимум, это точно попадает в раздел «не делайте так».
        «onUiBound» — а если я хочу разделять события по неймспейсам (задачи разные бывают), как поступать?

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

        По сути, это — глобальная переменная, а о том, что они — зло — написано миллионы тысяч раз. (как, к слову, и о сигнлтонах).

        Вот допустим, мне нужно иметь две модели (пользователь), никак между собой не связанные? Избыточно использовать список? Избыточно городить две одинаковые модели?
        И это только по тому, что описано в статье.

        P.S. Возможно в том и проблема, что в статье описано не полностью.

        И да. Я всеми руками «за» то, чтобы не городить велосипед-клон бэкбона (потом дорастете до марионета), а потратить силы и время на дописывание уже существующих.


        1. kahi4
          11.11.2015 15:37

          Ну и да. Не вижу, как тут реализовать наследование. А без него — сразу крест. Ну и опять же — где поддержка es6 классов, хотя бы в перспективе?

          Вы не подумайте, я не нахваливаю, бэкбон, у него своих проблем вагон и еще два вагона. Но jiant явно бросает ему вызов, поэтому и сравнение с ним.


          1. Vecnas
            11.11.2015 16:10

            Не совсем понимаю в чем схожесть с бэкбоном, так как там приходится писать много избыточного кода на реализацию всех функций модели, а здесь это делается автоматически. То что декларируется модель с ее полями — это только внешнее и на мой взгляд позитивное проявление, служащее документированию. Так можно и интерфейсы какого-нибудь C# или джавы обвинить в плагиате с бэкбона )

            При чем тут золушкина туфелька тоже не понял — интерфейс не переиспользуется не-пойми для чего, а каждый объект описывается своим. Как раз эта схожесть подхода катастрофически удобна, хотя и ломает стереотипы. Стоит только попробовать. Я сам не сразу решился на такой кардинальный шаг.

            Про глобальность — во-первых модели это не переменная, это API, доступ к моделям происходит через функции. Таким образом недостатка «а кто это поменял» здесь нет — при отладке любой доступ легко отслеживается по стек трейсу, либо нажатием ctrl+alt+f7 в той же Идее, чего никак не скажешь про другие фреймворки. Синглтон в приложении к моделям это всего лишь объект, встречающийся в приложении один раз, а не тот классический статический объект.

            Наследование в функциональном языке — до сих пор делается костылями. И не использование этих костылей я не считаю недостатком. А es6 классы это просто чуть подкрашенная обертка вокруг все тех же костылей. Давайте уже честно писать на функциональном языке функциями. Но если мне все же захочется унаследоваться сейчас, напишу вот так:

            var modelBase = {...}
            var modelInherited = $.extend({}, modelBase, {extra-properties-for-child});
            

            Но вообще метод add или updateAll возвращает объект модели, и при честном использовании классов es6 — я просто создам этот объект в коде и верну его (для репозиториев). Можно сказать, что модель это java reflection, если знакомо — и по нему воссоздается класс объекта.

            Про двух пользователей чуть позже.


            1. kahi4
              11.11.2015 16:26

              Давайте уже честно писать на функциональном языке функциями

              Давайте не называть ООП язык чисто функциональным. То, что там прототипно-ориентированный подход не означает, что это не ООП, тем более, что это чисто-функциональный.

              Про глобальность — во-первых модели это не переменная, это API, доступ к моделям происходит через функции.

              Через такие же глобальные. От того, что ты не напрямую получаешь её, а через функцию — лучше от этого не становится. Опять же. В одном месте мне нужен пользователь с ID = 1, в другом — с ID = 3, а они синглтоны… Впрочем, вы обещали это позже рассмотреть.

              Как раз эта схожесть подхода катастрофически удобна, хотя и ломает стереотипы

              Имеется ввиду то, что две абсолютно разные по идеологии абстракции используются с одним интерфейсом, вдобавок, неявно определяют. Написали update — получили один экземпляр. Написали fetch (или что там, в примерах в статье не вижу) — получили коллекцию. Взорвали себе мозг, забыв в какой-то момент, один у тебя объект, или репозиторий.

              «во-первых» а где во-вторых? простите, не удержался.

              Ну а теперь о схожести с бэкбоном.

              app.models.loggedUser = {
                  // auto-added function, declared for readability
                  update: function(obj) {},
              
                  // model fields
                  firstName: function(val) {},
                  lastName: function(val) {},
                  id: function(val) {}
                }
              

              и
              const User = Backbone.model.extend({ 
                firstName: function() {..}
              });
              

              Правда, в чем схожесть между двумя фреймворками, решающими одинаковую задачу похожими методами?

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

              Каких же? Тех же, что и у вас, реализующих бизнес-логику? Системные функции типа update, save и прочее — есть из коробки.


              1. Vecnas
                11.11.2015 16:48

                Сразу хочу сказать «спасибо за комментарии», все написанное очень ценно и интересно.
                По делу — Например, в бэкбон вот так:

                book.set("title", "A Scandal in Bohemia");
                

                а здесь вот так:
                book.title("A Scandal in Bohemia");
                

                Выглядит похоже. Но во втором случае есть автозавершение от IDE и это гарантирует что я не описался в строчной переменной и сэкономило мне от секунды до нескольких написания. Казалось бы — мелочь, но сколько времени экономится и потенциальных ошибок обходится. Аналогично get.
                Вообще когда я начинал jiant — я не смог найти ни одного дружащего с автозавершением js фреймворка, поэтому начал свой )
                По объявлению интерфейса — В джаве тоже очень похожая запись будет интерфейса. А в С# еще больше.
                И вот за этими "..." внутри бэкбон функции скрывается тоже ведь строчка кода, которую надо написать или скопировать. Это объявление не пустое.
                Насчет изобретения велосипеда — Форду или кому-то еще когда-то не понравились все имевшиеся на тот момент автомобили и он сделал свой. И неплохо получилось.
                Взорвать мозг, забыв что там, довольно сложно, так как всегда можно взглянуть на объявление. Плюс по смыслу обычно всегда понятно, не знаю почему, может проектировать удачно выходит. Уже название модели подсказывает что это такое и как используется.


                1. kahi4
                  11.11.2015 17:31

                  book.set(«title», «A Scandal in Bohemia»);

                  Не поспоришь — это косяк бэкбона, который меня подбешивает. Впрочем, очень небольшой патч позволяет избежать этого.
                  И вот за этими "..." внутри бэкбон функции скрывается тоже ведь строчка кода

                  А в вашем случае нет? Что в бэкбоне, что в jiant — там же бизнес-логика, естественно там есть код. А стандартные есть всегда


                  1. Vecnas
                    11.11.2015 17:41

                    нет кода, в этом и фишка — просто пустые функции, и find, list тоже. Если речь о сеттерах/геттерах конечно или о поиске по коллекции


        1. Vecnas
          11.11.2015 16:28

          Великая благодать Jiant'а в том что вместо программирования мы проектируем, и в конце получаем работающий код.
          Попробую этот подход и здесь.
          Два пользователя — допустим, такой сценарий — пользователь, пишущий комментарий, и пользователь, опубликовавший запись )
          Они идентичны по параметрам, поэтому да, один репозиторий, допустим такой:

          user: {
            add: function(obj) {},
          
            name: function(val) {},
            id: function(val) {}
          }
          

          Мы уже видим что у нас в приложении есть объект пользователя с именем и идентификатором, далее есть варианты на выбор, какой больше соответствует религии данного программиста:
          1) Объявим модель env с полями author, reviewer и выставим туда нужных пользователей
            env: {
              author: function(val) {},
              reviewer: function(val) {}
            }
            jiant.onUiBound(app, function($, app) {
              var m = app.models;
            // откуда-то получили, скажем с сервера данные об этих пользователях
              m.env.author(m.user.add(authorData)[0]);
              m.env.reviewer(m.user.add(reviewerData)[0]);
            });
          

          2) Аналогично 1му, но методы доступа помещаем прямо в user:
            user: {
              author: function() {return this.all()[0]},
              reviewer: function() {return this.all()[1]}
            }
           ....
            m.user.add([author, reviewer]);
          

          3) Добавить поле пользователю role, выставлять его и искать по нему
            user: {
              role: function(val) {},
              findByRole: function(val) {}
            }
            ....
            m.user.add(...);
            m.user.findByRole("author");
          

          3й способ расширяем, 1й почти как новые объекты, но вместо new — add, плюс перед new — возможность увидеть сразу список полей модели


          1. kahi4
            11.11.2015 16:41

            И это «меньше чем в бэкбоне» и изящное решение? У нас с вами сильно разные представления об изящности…

            К слову. Допустим, вам таки нужно два экземпляра с одинаковым id. Например, вы хотите посчитать диф между старой и новой версией (два человека одновременно редактируют запись). А в jiant заложено на уровне архитектуры невозможность такого (на сколько я понимаю — исходили из идеи «одна сущность может быть только в одном экземпляре). Да, можно сделать модель model_old, model_new — но это костыль


            1. Vecnas
              11.11.2015 16:54

              Тогда мне можно код аналогичный бэкбона, для сравнения? Со всеми декларациями переменных.
              Насчет одинакового ида — как раз здесь это плевое дело — при обновлении переменной передаются старое и новое значения. Допустим, мне прилетело по вебсокетам обновление на запись, тогда у меня сработает вот такой код:

                app.models.record.text.on(function(record, newText, oldText) {
                  ...
                }
              

              Где oldText — текущее значение, newText — новое. Подавляющее большинство задач в jiant решается на порядок меньшим объемом кода, это кстати из отзывов реальных людей (что-нибудь типа «почему тут так мало кода»), попробовавших на нем писать, а не реклама.


              1. kahi4
                11.11.2015 17:42

                const User = Backbone.Model.Extend({
                  url: '/users.json'
                });
                
                let author = new User(id); // можно передать obj
                let reviewer = new User(id);
                


                Сравнение тут так же очевидно, покуда у вас есть два объекта, которые нужно сравнить. Например, если пришел с сервера:
                const User = Backbone.Model.Extend({
                  url: '/users.json',
                  events: 
                    change: function(arg, old_val, new_val) { } // с точностью до аргументов
                    'change:name': () => {..} // следить за изменением только name
                });
                


                Бэкбон не панацея. В нем есть много узких мест, много чего хотелось бы переделать (все таки ему 5 лет), хотелось бы иметь более простой доступ к аргументам и прочее, но при этом в нем, оптимизированном до дыр, очень очень много готового, удобного, проектов на нем огромное количество, а так же большое комьюнити, в котором можно найти ответы практически на все возникающие при использовании вопросы.

                Вы же предлагаете новый молодой фреймворк с сомнительными преимуществами (как минимум, из этой статьи их не видно совершенно) с нулевым комьюнити. Да, попытки улучшить мир — похвальные, сделать разработку удобнее и проще — отличное дело, а запускать своими силами, не будучи гуглом или фейсбуком свой фреймворк ужасно сложно, в данном деле я вам желаю успеха. Но, к сожалению, совершенно не видно коммерческой ценности против прочих решений, коих сейчас миллионы.


                1. Vecnas
                  11.11.2015 17:51

                  Я попытаюсь ) Продолжу писать о других компонентах фреймворка.
                  Лучше попытаться и обломаться чем не попытаться.
                  По-крайней мере ниша нормального фреймворка на javascript сейчас пуста, то что доминирует — такое убожество, что просто приглашает попытаться.
                  Если мне удастся найти несколько человек, заинтересованных в новом фреймворке, это уже комьюнити для начала.
                  Присоединяйтесь, хотя бы попробуйте )


                1. Emily_Rose
                  12.11.2015 15:50

                  Если хотите более умные бэкбон модели то предлагаю вам посмотреть на ampersand-model. У них есть и другие компоненты, но мы используем именно модели в ангуляр проекте.


            1. Vecnas
              11.11.2015 16:57

              изящество в том что автор и ревьюер инкапсулированы внутри модели, и для внешнего использования вызывается всего-лишь
              env.author()
              или
              env.reviewer()
              это действительно удобно и изящно


          1. tamtakoe
            11.11.2015 18:57

            Великая проблема в том, что на рынке полно хороших программистов, а вот адекватных проектировщиков как раз таки нет.


            1. Vecnas
              11.11.2015 19:30

              Увы, я тоже встречал таких «хороших» (в кавычках) программистов — трудолюбивых (без кавычек) и готовых 8 часов писать 100 строк кода вместо того чтобы подумать 1 час и потом за 10 минут написать 3 строки. К счастью поувольняли(сь), но до сих пор мы за ними разгребаем их «творения». А многие могли бы делать хорошо, но не хотят думать.


  1. Vecnas
    11.11.2015 15:02

    После них не работает к сожалению.
    Касательно примеров — часть коммерческие приложения и чтобы дать на них ссылку мне надо получить разрешение. Из того что доступно — достаточно крупное — игрушка http://frombeyond-game.com, на которой собственно отлаживалась и проверялась на жизнеспособность в произвольном окружении концепция, домашняя страница и сама игра там реализованы полностью на jiant.
    Также есть локально реализация todo-mvc, в ближайшие дни выкладываю на github (думал уже выложил ) )


  1. Emily_Rose
    12.11.2015 15:41

    Много разных моделей проверял смотрел, для мeня лучше всех кажеться ampersand-model. Это как бэкбоун на стероидах. Завернули у себя в ангуляровский сервис и все очень хорошо. Мне не совсем понятно как у вас реализованы derived fields, кэшируемые и нет. Есть ли у вас понятие проксированя модели в REST API? Если да, то как определаються поля которые нужно передать на сервер а какие нет? Проверка типов и валидация? Значения по умолчанию?


    1. Vecnas
      12.11.2015 17:36

      Почти всего этого нет, не было необходимости в реальных проектах, а добавлял только то что нужно или что используется чаще чем в 10% случаев (как правило 90% функционала фреймворков работает для 10% юз кейсов, ну и 10% для 90%, этого хотелось избежать)

      1) derived — это как раз функции с реализацией, типа

      fullName: function() {return this.firstName + " " + this.lastName()}
      


      2) кэша на них нет, ситуация где значение такого поля используется больше 1-2 раз мне так с ходу не придумывается и не попадалась, но наверное бывает

      3) таким образом как в ampersand-model передача на сервер не сделала, я думал над этим, но пока не ощутил необходимости в таких функциях именно в модели. Собственно отправка модели на сервер выполняется так:
        app.ajax.doSearch(app.models.searchParams, callback);
        // или
        app.ajax.save(app.models.listing.all(), callback);
        // или
        app.ajax.save(app.models.listing.listByTp("some type"), callback);
      

      вместо ссылки на синглтон модель из примера можно подставить конкретный элемент коллекции, либо можно послать всю коллекцию

      4) отправляются все геттеры модели кроме объявленных как jiant.transient — эти на сервер не посылаются (и кроме derived)

      5) задача валидации не входит в модель, для этого есть jquery плагин validate

      6) проверки типов тоже нет, в разрабатываемых проектах ни разу не возникло ситуации чтобы я подумал что она нужна

      7) значения по умолчанию — вот это изредка возникает, и этого сейчас нет, чтобы установить какие-то начальные значения, нужно их ставить через сеттеры или через update()

      А вообще именно ampersand-model мне не попадался, искренне благодарю за наводку. Он действительно хорошо расширяет бэкбон и надо будет поизучать. Единственно — синглтон приложение (если я верно понял) — это несерьезно. У нас повсюду минимум два приложения крутится на страницах — одно это чат, другое основное, а кое-где и по 3-4, с разных доменов, и они прекрасно друг с другом уживаются, включая совместную работу. Ну и хэш навигация у них как почти везде сделана явно в лоб. В этом jiant сильно обогнал их.


      1. Vecnas
        12.11.2015 17:42

        в примере к 1 пункту опечатка, должно быть

          fullName: function() {return this.firstName() + " " + this.lastName()}
        

        Привык что Идея авто-завершает функции скобками и не поставил на автомате )