РЭДЛАЙН
Лучшие решения для Вас и Вашего бизнеса!
На нашем сайте вы можете получить информацию о веб-разработке, обслуживании и продвижении сайта. Интернет-маркетинге. SEO (поисковой оптимизации). Контекстной и медийной рекламе в Интернете. SMM. Регистрации доменов и хостинговых услугах. И современном дизайне сайтов. Вообщем того что касается веб-разработки, а также много другой полезной информации из мира интернета, бизнеса и интернет-технологий...
Создаем доступные и современные сайты, которые работают! Обслуживаем и эффективно продвигаем интернет-проекты с 2006 года!
Главная Программирование HTML 5: Пример использования knockout, amplify и underscore или JsSite как стартовая архитектура для сайта (обновление)


HTML 5: Пример использования knockout, amplify и underscore или JsSite как стартовая архитектура для сайта (обновление)

Цель

В одной из прошлых статьей было рассказано о nuget-пакете под названием JsSite. В последнее время достаточно часто и продуктивно пришлось работать с этим пакетом, и как  следствие, сам пакет претерпел большое количество изменений. Цель данной статьи, описать возможности (в том числе и новые), которые предоставляет данный набор скриптов.

Еще раз хочу предупредить, что JsSite всего лишь простой пример построения архитектуры сайта с использованием knockoutjs, amplifyjs, moment и других полезных библиотек на JavaScript. Но этот пакет влючены некоторые полезные, по моему мнению, контролы, в частности DataSource? о котором и пойдет речь в этой статье.

Пример использования или How to use.

В примере будем строить MVC приложение, которое будет отображать список сотрудников (возьмем из nuget-пакета SampleData) с использованием AJAX, Web API и knockoutjs. Ключевой момент в том, чтобы не просто отображать данные, а разбить их на страницы, подключить простейший фильтр, и возможность задавать количество записей на странице.

Более того, очень хочется один раз написать сервис для работы с сущностью, а потом по возможности использовать его на разных страницах и/или в разных запросах (в том числе типа “Master/Details”).

Подготовка к работе

Создаем новое ASP.NET MVC приложение. Я выбрал новый (появился после  ASP.NET and Web Tools 2012.2) шаблон MVC4 Basic, в нем папки Controller и Model пусты. Проект я назову JsSitePackageDemo2, а вы как посчитаете нужным. Запустим обновление всех предустановленных пакетов, выполнив команду update-package в Package Manager Console. И после этого поставим несколько дополнительных пакетов:

1) jssite:

PM> Install-Package jssite
Attempting to resolve dependency 'toastr (≥ 1.1.4.2)'.
Attempting to resolve dependency 'jQuery (≥ 1.6.3)'.
Attempting to resolve dependency 'AmplifyJS (≥ 1.1.0)'.
Attempting to resolve dependency 'knockoutjs (≥ 2.2.1)'.
Attempting to resolve dependency 'Knockout.Mapping (≥ 2.4.0)'.
Attempting to resolve dependency 'underscore.js (≥ 1.4.3)'.
Attempting to resolve dependency 'Moment.js (≥ 1.7.2)'.
Successfully installed 'toastr 1.1.5'.
Successfully installed 'AmplifyJS 1.1.0'.
Successfully installed 'Knockout.Mapping 2.4.0'.
You are downloading underscore.js from Jeremy Ashkenas, the license agreement to which is available at
https://github.com/documentcloud/underscore/blob/master/LICENSE. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'underscore.js 1.4.4'.
Successfully installed 'Moment.js 1.7.2'.
Successfully installed 'JsSite 0.4.2'.
Successfully added 'toastr 1.1.5' to JsSitePackageDemo2.
Successfully added 'AmplifyJS 1.1.0' to JsSitePackageDemo2.
Successfully added 'Knockout.Mapping 2.4.0' to JsSitePackageDemo2.
Successfully added 'underscore.js 1.4.4' to JsSitePackageDemo2.
Successfully added 'Moment.js 1.7.2' to JsSitePackageDemo2.
Successfully added 'JsSite 0.4.2' to JsSitePackageDemo2.

PM>

2) SampleData:

PM> Install-Package SampleData
Successfully installed 'SampleData 1.2.2'.
Successfully added 'SampleData 1.2.2' to JsSitePackageDemo2.

PM>

Подключаю новые CSS, которые появились с установленным пакетом JsSite (см. строка 2):

   1:  bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"
   2:      , "~/Content/toastr.css", "~/Content/site.pages.css"));

Можно теперь приступить непосредственно к кодированию. Если учесть, что контролеров в этом шаблоне нет, а в файле RouteConfig.cs прописан маршрут на контролер Home:

   1:  routes.MapRoute(
   2:      name: "Default",
   3:      url: "{controller}/{action}/{id}",
   4:      defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
   5:  );

Назревает вопрос, зачем разработчики оставили его или зачем удалил Home контролер? Ну, да ладно, суть не в этом. Меняю наименование контролера на “Site” и создаю новый контролер с этим названием.

jssite-demo2-1

Добавлю к методу Index представление (View). На этом представлении и будем упражняться в написании кода. Первым делом добавлю ссылки на скрипты библиотек в секцию scripts. Если учесть что jQuery уже установлена (bundle/jquery в шаблоне), то мне остается добавить ссылки на сторонние библиотеки и на скрипты из пакета JsSite:

   1:  @section scripts
   2:  {
   3:      <!-- third-party library -->
   4:      <script src="~/Scripts/toastr-1.1.5.min.js"></script>
   5:      <script src="~/Scripts/amplify.min.js"></script>
   6:      <script src="~/Scripts/moment.min.js"></script>
   7:      <script src="~/Scripts/underscore.min.js"></script>
   8:      <script src="~/Scripts/knockout-2.2.1.js"></script>
   9:      <script src="~/Scripts/knockout.mapping-latest.js"></script>
  10:  
  11:      <!-- jssite and project library -->
  12:      <script src="~/Scripts/app/site.core.js"></script>
  13:      <script src="~/Scripts/app/site.bindingHandlers.js"></script>
  14:      <script src="~/Scripts/app/site.controls.js"></script>
  15:  }

Я пока не заморачиваюсь на оптимизацию скриптов: “склеивание” и “сжатие”, но в реальном проекте без этого не обойтись. Один из вариантов решения был описан ранее.

В папке App есть еще один файл, который я не добавил на страницу. Дело в том, что этот файл всего лишь пример написания сервиса для DataSource. Мы займемся написанием сервиса чуть позже. Сначала серверная часть.

Web API + OData

Создаем новый API-контролер назовем его PersonController:

jssite-demo2-2

Не обойдите вниманием, шаблон – “API controller…”. Вот так выглядит код этого контролера после некоторых доработок причем несложных, но не окончательных:

   1:  publicclass PersonController : ApiController {
   2:  privatereadonly List<Person> _list;
   3:   
   4:  public PersonController() {
   5:          _list = People.GetPeople();
   6:      }
   7:   
   8:  // GET api/person
   9:  public IEnumerable<Person> Get() {
  10:  return _list;
  11:      }
  12:   
  13:  // GET api/person/5
  14:  publicstring Get(int id) {
  15:  return"value";
  16:      }
  17:   
  18:  // POST api/person
  19:  publicvoid Post([FromBody]stringvalue) {
  20:      }
  21:   
  22:  // PUT api/person/5
  23:  publicvoid Put(int id, [FromBody]stringvalue) {
  24:      }
  25:   
  26:  // DELETE api/person/5
  27:  publicvoid Delete(int id) {
  28:      }
  29:  }

Строка 2: создаем переменную для хранения списка пользователей. Не забудьте добавить namespace SampleData.

Строки 4-6: в конструкторе наполняем список.

Если воспользоваться прекрасной утилитой Fiddler, то можно протестировать сервис, отправив запрос:

jssite-demo2-3

и получить ответ в:

jssite-demo2-4

OData – теперь это просто

Небольшое лирическое отступление. Протокол OData теперь есть в MVC4 (не полная реализация, но и это уже не мало). Для того, чтобы превратить наш PersonController в контролер, который будет понимать OData запросы, надо установить еще один nuget-пакет Microsoft.AspNet.WebApi.OData, который добавит магический атрибут Queryable.

PM> Install-Package Microsoft.AspNet.WebApi.OData
Attempting to resolve dependency 'Microsoft.Net.Http (≥ 2.0.20710.0 && < 2.1)'.
Attempting to resolve dependency 'Microsoft.AspNet.WebApi.Client (≥ 4.0.20710.0 && < 4.1)'.
Attempting to resolve dependency 'Newtonsoft.Json (≥ 4.5.6)'.
Attempting to resolve dependency 'Microsoft.AspNet.WebApi.Core (≥ 4.0.20710.0 && < 4.1)'.
Attempting to resolve dependency 'Microsoft.Data.OData (≥ 5.2.0 && < 5.3.0)'.
Attempting to resolve dependency 'System.Spatial (= 5.2.0)'.
Attempting to resolve dependency 'Microsoft.Data.Edm (= 5.2.0)'.
You are downloading System.Spatial from Microsoft Corporation, the license agreement to which is available at
http://go.microsoft.com/?linkid=9809688. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'System.Spatial 5.2.0'.
You are downloading Microsoft.Data.Edm from Microsoft Corporation, the license agreement to which is available at
http://go.microsoft.com/?linkid=9809688. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.Data.Edm 5.2.0'.
You are downloading Microsoft.Data.OData from Microsoft Corporation, the license agreement to which is available at
http://go.microsoft.com/?linkid=9809688. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.Data.OData 5.2.0'.
You are downloading Microsoft.AspNet.WebApi.OData from Microsoft, the license agreement to which is available at
http://www.microsoft.com/web/webpi/eula/aspnet_and_web_tools_2012_2_RTW_EULA_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebApi.OData 4.0.0'.
Successfully added 'System.Spatial 5.2.0' to JsSitePackageDemo2.
Successfully added 'Microsoft.Data.Edm 5.2.0' to JsSitePackageDemo2.
Successfully added 'Microsoft.Data.OData 5.2.0' to JsSitePackageDemo2.
Successfully added 'Microsoft.AspNet.WebApi.OData 4.0.0' to JsSitePackageDemo2.

PM>

Если вы не знакомы с протоколом OData, то советую ознакомиться на упомянутом выше сайте. Там же вы можете найти документацию по использованию (спецификацию). После установки пакета можно поставить атрибут [Queryable] над методом Index в контролере PersonController. 1: [Queryable] 2: public IEnumerable<Person> Get() { 3: return _list; 4: }

Что это дает? Это на самом деле, не побоюсь этого слова, это “революционная” технология построения запросов непосредственно в строке браузера, то есть формировать запросы к БД можно из строки браузера (!) напрямую. Спецификация протокола очень большая, чтобы рассказать о ней в одном маленьком абзаце, но чтобы было понятен принцип, ознакомьтесь со схемой:

jssite-demo2-5

Начало развития OData берет в холодном феврале 2009 года, но на сколько я помню, такой принцип обработки запросов был озвучен еще раньше – в октябре 2007 года (проект “Астория”). На данный момент не все команды по спецификации поддерживаются в ASP.NET MVC 4 Web.API, а список поддерживаемых вы можете найти на ASP.NET.

Прелесть данного подхода в том, что “запрос выполняется непосредственно на SQL сервере”. Это лирическое отступление имело место быть, потому что вчера Microsoft.AspNet.WebApi.OData вышел в статусе Release под номер версии 4.0.0.

Web API

Вернемся к наши Web.API. Для того, чтобы сервис смог вернуть данные разбитые на страницы (это же наша цель, правда?), надо в метод Get() “опустить” как минимум один параметр – номер страницы (pageIndex), а если вы хотите управлять количеством записей на странице со стороны клиента, то второй параметр должен быть – размер страницы (pageSize). У вас не получится без перенастройки Web.API маршрутов “протолкнуть” упомянутые параметры в метод вызова. Настроим маршрут. Я добавил один новый маршрут (в файле WebApiConfig) для того, чтобы Web.API стал “понимать” новые параметры “номер страниц” и “размер страниц” (см. строки 3-7), а не только идентификатор (Id см. строки 9-12) :

   1:  publicstaticclass WebApiConfig {
   2:  publicstaticvoid Register(HttpConfiguration config) {
   3:          config.Routes.MapHttpRoute(
   4:              name: "PersonApi",
   5:              routeTemplate: "api/{controller}/{index}-{size}",
   6:              defaults: new { index = 0, size = 10 }
   7:          );
   8:   
   9:          config.Routes.MapHttpRoute(
  10:              name: "DefaultApi",
  11:              routeTemplate: "api/{controller}/{id}",
  12:              defaults: new { id = RouteParameter.Optional }
  13:          );
  14:      }
  15:  }

Строке 5: обратите внимание на “черточку”, она нам пригодится позже (вообще-то она для “красоты”, чтобы было проще ссылаться в статье). После того как маршруты проложены настроены, можно поработать над методом Get контролера PersonController.

   1:  public HttpResponseMessage Get(int? index, int? size) {
   2:      var items = _list.AsQueryable();
   3:  if (index.HasValue && size.HasValue) {
   4:          items = items.Skip(index.Value * size.Value)
   5:              .Take(size.Value);
   6:      }
   7:  if (items.Any()) {
   8:          var data = items.ToArray();
   9:          var result = new ApiResult { Items = data, Total = _list.Count() };
  10:  return Request.CreateResponse(HttpStatusCode.OK, result);
  11:      }
  12:  return Request.CreateResponse(HttpStatusCode.BadRequest); ;
  13:  }

Строка 1: Возвращаем не просто коллекцию объектов, а обернутую в специальный класс HttpResponseMessage, который дает множество полезных штучек, как например, статус операции запроса. А также добавляем параметры в сигнатуру метода.

Строка 3-6:  Если “номер страницы” и  “размер страницы” получены, осуществляем выборку.

Строка 9: Создаем возвращаемый объект (см. следующий листинг). Можно использовать и анонимный тип, но нравится типизация:

   1:  publicclass ApiResult {
   2:  public IEnumerable<Person> Items { get; set; }
   3:  publicint Total { get; set; }
   4:  }

Далее по листингу метода Get().

Строка 10: Возвращает полученный результат с указанием статуса. Обратите внимание на параметр “Items” и “Total”. Для того, чтобы пейджер заработал, ему надо знать сколько всего записей. Эти параметры использует site.controls.DataSource() из пакета JsSite.

Следующим этапом – JavaScript!

Модель сервиса site.services.person.js

Раз уже API-сервис готов, то время пришло для js-сервиса. При установке пакета JsSite в папке App также появляется файл site.services.js. Как уже говорилось, это демонстрационный пример сервиса, который нужен для работы site.controls.DataSource().  Я его переработал, адаптировав под класс Person, и поменял его название. Теперь он называется site.services.person.js и содержит он много строк. Методы addPerson, updatePerson, deletePerson я не стал реализовывать, но я всё равно приведу файл целиком, а после разберем по строкам:

   1:  (function (site) {
   2:   
   3:  "use strict";
   4:   
   5:      site.services.init = function () {
   6:   
   7:  //#region service Person
   8:          site.amplify.request.define("loadPerson", "ajax", {
   9:              url: "api/Person/{0}-{1}",
  10:              dataType: "json",
  11:              type: "GET"
  12:          }),
  13:           site.amplify.request.define("addPerson", "ajax", {
  14:               url: "api/Person/",
  15:               dataType: "json",
  16:               cache: false,
  17:               type: "POST"
  18:           }),
  19:           site.amplify.request.define("updatePerson", "ajax", {
  20:               url: "api/Person",
  21:               dataType: "json",
  22:               cache: false,
  23:               type: "PUT"
  24:           }),
  25:           site.amplify.request.define("deletePerson", "ajax", {
  26:               url: "api/Person",
  27:               dataType: "json",
  28:               cache: false,
  29:               type: "DELETE"
  30:           });
  31:  //#endregion
  32:   
  33:      }();
  34:   
  35:      site.services.person = function () {
  36:  var
  37:              loadPerson = function (params, back) {
  38:  return site.amplify.request({
  39:                      resourceId: "loadPerson",
  40:                      data: {index: params.index(), size: params.size()},
  41:                      success: function (json) {
  42:  //  -> here you code json-data processing <-  
  43:  if (json && json.Items) {
  44:                              site.logger.success("Loaded: "
  45:                                  + json.Items.length
  46:                                  + " Total: " + json.Total);
  47:                          }
  48:  if (typeof back == "function") {
  49:  params.total(json.Total);
  50:                              back.call(this, json.Items);
  51:                          }
  52:                      },
  53:                      error: function (message, status) {
  54:                          site.logger.error(message, status);
  55:                          back.call(this);
  56:                      }
  57:                  });
  58:              },
  59:              addPerson = function (jsonPerson) {
  60:  return site.amplify.request({
  61:                      resourceId: "addPerson",
  62:                      data: jsonPerson,
  63:                      success: function (json, status) {                        
  64:  //  -> here you code json-data processing <-                        
  65:  if (typeof back == "function") {
  66:                              back.call(this, json);
  67:                          }
  68:                      },
  69:                      error: function (message, status) {
  70:                          site.logger.error(message, status);
  71:                          back.call(this);
  72:                      }
  73:                  });
  74:              },
  75:              updatePerson = function (jsonPerson) {
  76:  return site.amplify.request({
  77:                      resourceId: "updatePerson",
  78:                      data: jsonPerson,
  79:                      success: function (json, status) {                        
  80:  //  -> here you code json-data processing <-                        
  81:  if (typeof back == "function") {
  82:                              back.call(this, json);
  83:                          }
  84:                      },
  85:                      error: function (message, status) {
  86:                          site.logger.error(message, status);
  87:                          back.call(this);
  88:                      }
  89:                  });
  90:              },
  91:              deletePerson = function (jsonPerson) {
  92:  return site.amplify.request({
  93:                      resourceId: "deletePerson",
  94:                      data: jsonPerson,
  95:                      success: function (json, status) {                        
  96:  //  -> here you code json-data processing <-                        
  97:  if (typeof back == "function") {
  98:                              back.call(this, json);
  99:                          }
 100:                      },
 101:                      error: function (message, status) {
 102:                          site.logger.error(message, status);
 103:                          back.call(this);
 104:                      }
 105:                  });
 106:              };
 107:   
 108:  return {
 109:              load: loadPerson,
 110:              put: updatePerson,
 111:              post: addPerson,
 112:              del: deletePerson
 113:          };
 114:   
 115:      }();
 116:   
 117:  })(site);

Внимание: Не забудьте добавить ссылку на этот скрипт на Index.cshtml.

Итак, что же делает этот код? По порядку.

Строка 5-33: Инициализируем сервис. Настройка amplify под работу с Web.API. Вот тут-то и пригодилась “галочка” (см. строка 9).

Строка 35-106: Сервис для работы с сущностью Person. Все действия в одном месте и, что самое главное, одни раз! Далее DataSource будет брать этот сервис и работать с его методами.

Ах, да! Самое главное! Вы можете приватные методы называть как хотите, а вот наружу должны быть “выставлены” методы именно с таким название как указано в строке 109-112: “load”, “put”, “post” “del”. Это важно!

Получение данных или Load Data Method

Строки 37-58 в предыдущем листинге задает метода получения списка пользователей. В этом методе используется название идентификатор “loadPerson”, который был инициализирован в ранее в строках 8-11. В строке 40 полученные параметры из DataSource (“index” и “size”) передаем в запрос, amplifyjs расставит параметры в соответствии с указание (см. строка 9) через “черточку”.

Строка 41-56: Обработчики полученных данных. В строках 43-47 проверяем полученных объект и выдаем сообщение, а далее в строках 48-51 возвращаем полученные данные (json.Items) в DataSource.

Строка 55: Если возникает ошибка сервиса, то возвращаем в DataSource “ничего” :)

А где же DataSource или покажите ViewModel представления

Самым простым кодом, в примере использования будет js-viewModel для моей страницы Index.cshtml. По большому счету, он содержит всего один контрол – DataSource, именно к нему и осуществляется привязка на форма (в следующем абзаце). Вот ViewModel:

   1:  $(function () {
   2:   
   3:  "use strict";
   4:   
   5:      site.vm.viewModel = function () {
   6:          var clock = new site.controls.Clock(),
   7:              meta = new site.fw.Metadata(
   8:  "Demo JsSite",
   9:  "Демонстрация работы библиотеки",
  10:  "http://www.calabonga.net"),
  11:              ds = new site.controls.DataSource(
  12:                  {
  13:                      autoLoad: true,
  14:                      service: site.services.person
  15:                  }
  16:              );
  17:   
  18:  return {
  19:              ds: ds,
  20:              meta: meta,
  21:              clock: clock
  22:          };
  23:      }();
  24:   
  25:      ko.applyBindings(site.vm.viewModel);
  26:   
  27:  });

Строка 6: Создаем объект “часы”. Я его добавляю на форму обычно первым, чтобы перед тем как начать программирование проверить, что скрипты правильно подключены, инициализированы и привязка HTML (applyBindings) настроена корректно.

Строка 7: Создаем для красоты объект “метаданные”.

Строка 11: И, наконец, создаем тот самый объект “DataSource”, в конструктор которого “опускаем” параметры “autoLoad” со значением “true”, кстати, это значение по умолчанию. А вторым параметром “service” значение которого, как раз и является наш person-сервис, описанный выше.

Все параметры и возможности DataSource планируется описать в следующей статье.

Вернемся на форму

Для того, чтобы полученные данные отобразились на представлении, изменим содержание Index.cshtml. Добавим разметку, обратите внимание, что объект привязки DataSource в строке 14 и 23:

   1:  <h2 data-bind="text: meta.title"></h2>
   2:  <p data-bind="text: meta.description"></p>
   3:  <div data-bind="text: clock.time"
            style="color:#888; position: fixed; top:20px; left:50%;"></div>
   4:   
   5:   
   6:  <table>
   7:      <thead>
   8:          <tr>
   9:              <th>Name</th>
  10:              <th>Age</th>
  11:              <th>Country</th>
  12:          </tr>
  13:      </thead>
  14:      <tbody data-bind="foreach: ds.items">
  15:          <tr>
  16:              <td><span data-bind="text: Name"></span></td>
  17:              <td><span data-bind="text: Age"></span></td>
  18:              <td><span data-bind="text: Country"></span></td>
  19:          </tr>
  20:      </tbody>
  21:  </table>
  22:   
  23:  <div data-bind="pager: ds"></div>

И как результат:

jssite-demo2-6

Зависимости или Master/Details

Я создал API-сервиc и js-сервис для сущности “Department” (тоже есть в SampleData), и немного поправил ViewModel, чтобы при выборе подразделения менялся список сотрудников:

   1:  $(function () {
   2:   
   3:  "use strict";
   4:   
   5:      site.vm.viewModel = function () {
   6:  var clock = new site.controls.Clock(),
   7:              meta = new site.fw.Metadata(
   8:  "Demo JsSite",
   9:  "Демонстрация работы библиотеки",
  10:  "http://www.calabonga.net"),
  11:              queryParams = { DepartmentId: ko.observable(), size:4 },
  12:   
  13:              ds = new site.controls.DataSource(
  14:                  { autoLoad: false, service: site.services.person },queryParams),
  15:   
  16:              ds2 = new site.controls.DataSource({
  17:                  service: site.services.department
  18:              }),
  19:  
  20:              selectDepartment = function (item) {
  21:                  ds.queryParams.DepartmentId(item.Id);
  22:              };
  23:   
  24:          ds.queryParams.DepartmentId.subscribe(function() {
  25:              ds.load();
  26:          });
  27:   
  28:  return {
  29:              select: selectDepartment,
  30:              ds2: ds2,
  31:              ds: ds,
  32:              meta: meta,
  33:              clock: clock
  34:          };
  35:      }();
  36:   
  37:      ko.applyBindings(site.vm.viewModel);
  38:   
  39:  });

Конечно же, пришлось поправить маршруты, чтобы новый параметр “departmentId” был доступен для PersonController. Да и сам метод Get у PersonController’а пришлось доработать, чтобы он “понимал” новый параметр и заработала связка “мастер/детали”.

Итак, в строке 11 создал параметр queryParams для DataSource, а в строка 14 его использую. В этой же строке отключена загрузка по умолчанию (autoLoad: false) для списка сотрудников. В queryParams также переопределен размер страниц (size) по умолчанию (равен 10) на новый размер 4.

Строки 16-18: Создал DataSource для сущности Department.

Строки 20-22: Функция обработки сlick по строке в таблице подразделений (см. листинг ниже строка 11).

Строки 29 и 30 выставляют новые классы и переменные наружу.

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

Добавил таблицу с подразделениями (строк 2-17).

   1:  <h2>Подразделения</h2>
   2:  <table>
   3:      <thead>
   4:          <tr>
   5:              <th>Id</th>
   6:              <th>Name</th>
   7:              <th>Description</th>
   8:          </tr>
   9:      </thead>
  10:      <tbody data-bind="foreach: ds2.items">
  11:          <tr data-bind="click: $root.select" style="cursor:pointer;" >
  12:              <td><span data-bind="text: Id"></span></td>
  13:              <td><span data-bind="text: Name"></span></td>
  14:              <td><span data-bind="text: Description"></span></td>
  15:          </tr>
  16:      </tbody>
  17:  </table>
  18:  <h2>Сотрудники</h2>
  19:  <div data-bind="pager: ds2"></div>
  20:   
  21:  <table>
  22:      <thead>
  23:          <tr>
  24:              <th>Id</th>
  25:              <th>Department</th>
  26:              <th>Name</th>
  27:              <th>Age</th>
  28:              <th>Country</th>
  29:          </tr>
  30:      </thead>
  31:      <tbody data-bind="foreach: ds.items">
  32:          <tr>
  33:              <td><span data-bind="text: Id"></span></td>
  34:              <td><span data-bind="text: DepartmentId"></span></td>
  35:              <td><span data-bind="text: Name"></span></td>
  36:              <td><span data-bind="text: Age"></span></td>
  37:              <td><span data-bind="text: Country"></span></td>
  38:          </tr>
  39:      </tbody>
  40:  </table>
  41:   
  42:  <div data-bind="pager: ds"></div>

jssite-demo2-7

Заключение

Что имеем в результате? Вся базовая логика работы с сущностью пишется один раз в одном месте. Достаточно гибкий способ устанавливать зависимости обеспечивает большой функционал при помощи knockout. Если кого-то заинтересовала разработка, то темой следующей статьи можно сделать “параметры, методы, договоренности при использовании DataSource” или вообще выложить DataSource, например, на github.com. В дальнейшем, планируется доработка DataSource для работы по протоколу OData, о котором было упомянуто в статье.

Загрузить

Если у вас возникли вопросы, предлагаю скачать проект с демонстрацией.

Подробнее: http://feedproxy.google.com/~r/blogmusor/~3/Z9_dLbaL-bA/112

HTML 5: Пример использования knockout, amplify и underscore или JsSite как стартовая архитектура для сайта (обновление) | | 2013-02-22 02:40:43 | | Программирование | | ЦельВ одной из прошлых статьей было рассказано о nuget-пакете под названием JsSite. В последнее время достаточно часто и продуктивно пришлось работать с этим пакетом, и как  следствие, сам пакет | РэдЛайн, создание сайта, заказать сайт, разработка сайтов, реклама в Интернете, продвижение, маркетинговые исследования, дизайн студия, веб дизайн, раскрутка сайта, создать сайт компании, сделать сайт, создание сайтов, изготовление сайта, обслуживание сайтов, изготовление сайтов, заказать интернет сайт, создать сайт, изготовить сайт, разработка сайта, web студия, создание веб сайта, поддержка сайта, сайт на заказ, сопровождение сайта, дизайн сайта, сайт под ключ, заказ сайта, реклама сайта, хостинг, регистрация доменов, хабаровск, краснодар, москва, комсомольск |
 
Дайджест новых статей по интернет-маркетингу на ваш email
Подписаться

Продающие сайты "под ключ"!

Наши сайты зарабытывают вам деньги. Landing-page. Эффективные продающие сайты точно в срок и под ключ! Всего от 14700 рублей
Подробнее...

Интернет-магазины и каталоги "под ключ"!

Эффективные и удобные инструменты торговли (электронной торговли) "под ключ". Продают, даже когда вы спите! Всего от 33800 рублей
Подробнее...

Комплексный интернет-маркетинг и продвижение сайтов

Максимальную эффективность дает не какой-то конкретный метод, а их комбинация. Комбинация таких методов и называется комплексным интернет-маркетингом. Всего от 8000 рублей в месяц
Подробнее...

Реклама в Yandex и Google

Контекстная реклама нацелена лишь на тех пользователей, которые непосредственно заинтересованы в рекламе Ваших услуг или товаров. Всего от 8000 рублей в месяц
Подробнее...

Social media marketing (SMM) — продвижение в социальных медиа

Реклама в Однокласcниках и на Mail.ru Создание, ведение и раскрутка групп и реклама ВКонтакте и Facebook. Всего от 8000 рублей в месяц
Подробнее...

Приглашаем к сотрудничеству рекламные агентства и веб-студии!

Внимание Акция! Приглашаем к сотрудничеству рекламные агентства и различные веб-студии России! Индивидуальные и взаимовыгодные условия сотрудничества.
Подробнее...

Ускоренная разработка любого сайта от 5 дней!

Внимание Акция! Ускоренная разработка любого сайта! Ваш сайт будет готов за 5-10 дней. Вы можете заказать разработку любого сайта "под ключ" за 5-10 рабочих дней, с доплатой всего 30% от его стоимости!
Подробнее...

Ждем новых друзей!

Внимание Акция! Ждем новых друзей! Скидка 10% на услуги по созданию и(или) обслуживанию вашего сайта при переходе к нам от другого разработчика.
Подробнее...

Приведи друга и получи скидку!

Внимание Акция! Приведи друга и получи скидку! Скидка 10% на услуги по созданию и(или) обслуживанию вашего сайта, если клиент заказавший наши услуги, пришел по Вашей рекомендации.
Подробнее...

1 2 3 4 5 6 7 8 9

Новые статьи и публикации


Услуги интернет компании Редлайн

Каждый человек имеет право на собственное мнение — при условии, что оно совпадает с нашим

Шоу Джордж Бернард - 1856-1950) - английский писатель. В своем творчестве ниспровергал догматизм и предвзятость, традиционность представлений

Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!

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

Заявка
Позвоните или оставьте заявку на сайте.


Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!


Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.


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


Поддержка
Сдача выполненых работ, последующие корректировки и поддержка при необходимости.

Остались еще вопросы? Просто позвоните и задайте их специалистам
с 2:30 до 11:30 по Мск, звонок бесплатный
Или напишите нам в WhatsApp
с 9:30 до 18:30 по Хабаровску
Или напишите нам в WhatsApp
Веб-студия и агентство комплексного интернет-маркетинга «РЭДЛАЙН» © 2006 - 2024

Профессиональная Веб-разработка. Создание сайтов и магазинов "под ключ" , а также по всей России и зарубежью. Продвижение и реклама. Веб-дизайн. Приложения. Сопровождение. Модернизация. Интеграции. Консалтинг. Продвижение и реклама. Комплексный Интернет-маркетинг.

Оставьте заявку / Задайте вопрос

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Заказать услугу

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Обратный звонок

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Подписка на дайджест новостей

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Заказать услуги со скидкой \ Бесплатная консультация







КАКИЕ УСЛУГИ ВАС ИНТЕРЕСУЮТ?

КАКИЕ ДОПОЛНИТЕЛЬНЫЕ УСЛУГИ ПОТРЕБУЮТСЯ?

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

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

Что нужно сделать, чтобы заказать создание сайта у нас?

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

Быстрый заказ \ Консультация

Для всех тарифных планов на создание и размещение сайтов включено:

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

Комплексная раскрутка работает в рамках стратегии развития вашего бизнеса в сети и направлена

Быстрый заказ \ Консультация

ЭФФЕКТИВНОЕ СОПРОВОЖДЕНИЕ (ПОДДЕРЖКА, ОБСЛУЖИВАНИЕ) САЙТОВ

Полный комплекс услуг по сопровождению сайтаМы оказываем полный комплекс услуг по сопровождению сайта: информационному и техническому обслуживанию и развитию Интернет сайтов.

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

Наша компания осуществляет техническую и информационную поддержку уже имеющихся сайтов. В понятие «поддержка сайтов» также входят услуги администрирования сайтов, обновления сайтов и их модернизация.

Быстрый заказ \ Консультация

Редизайн сайта и Адаптивный веб дизайн

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

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

Адаптивный дизайн сайтов и веб-приложений

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

На сегодняшний день адаптивный дизайн является стандартным подходом при разработке новых сайтов (или веб-приложений) и в идеале ваш сайт должен смотреться и функционировать так, как вы задумывали, на всём разнообразии устройств.

Быстрый заказ \ Консультация

Контекстная реклама в Яндекс и GoogleКонтекстная реклама - это эффективный инструмент в интернет маркетинге, целью которого является увеличение продаж. Главный плюс контекстной рекламы заключается в том, что она работает избирательно.

Реклама в поисковых системах Яндекс и Google. Профессиональная настройка рекламы и отслеживание эффективности!

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

Быстрый заказ \ Консультация

Скидка

1500 руб.
Заинтересовались услугами создания, обслуживания или продвижения вашей компании в Интернете?!
Получите 1500 руб.
за он-лайн заявку
Предложение ограничено.

После получения заявки с Вами свяжутся наши специалисты и уточнят все детали по интересующей вас услуге.
«Нажимая на кнопку "Получить скидку", я даю согласие на обработку персональных данных»
×
Получите 1500 рублей!
×
×