Работа с данными в JavaScript

от автора

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

От создания моделей на каждую форму я сразу отказался, поскольку при изменении одного поля, должно было каким-то образом меняться другое. Логика взаимодействия полей распространялась на 3 таблицы базы данных, из которых 2 хотя бы на половину заполнены данными и одна, в которую мне нужно передавать изменения. Поэтому я решил сделать собственное хранилище (навроде Store в Ext js). Суть заключалась в том, чтобы представить каждый узел объекта формата JSON в самостоятельную единицу. То есть принцип следующий: я получаю все 3 таблицы и создаю 3 дерева указателей, где каждый родитель является контекстом дочернего узла. Возможно, звучит, немного запутанно. Вот пример.

Заказчики

var Customers = [   {     "FirstName": "Customer first name 1",     "LastName": "Customer second name 1",     "Id": "1",     "JobSpecificsList": [       {         "Id": "1",         "Name": "Orders group 1"       },       {         "Id": "2",         "Name": "Orders group 2"       }     ],     "WorkTypes": [       "2",       "0",       "1"     ],     "PaymentTypes": [       "1",       "2",       "3"     ]   },   {     "FirstName": "Customer first name 2",     "LastName": "Customer second name 2",     "Id": "2",     "JobSpecificsList": [       {         "Id": "1",         "Name": "Orders group 1"       },       {         "Id": "61",         "Name": "Orders group 3"       },       {         "Id": "58",         "Name": "Orders group 4"       }     ],     "WorkTypes": [       "2",       "0"     ],     "PaymentTypes": [       "1"     ]   } ]  

Заказы

var orders = [   {     "Id":"1",     "CustomerId": "1",     "OrderName": "Order 1",     "Start": "2015-04-12T11:22:00.0000000",     "End": "2015-04-12T22:11:00.0000000"   },   {     "Id":"2",     "CustomerId": "2",     "OrderName": "Order 2",     "Start": "2015-04-12T11:22:00.0000000",     "End": "2015-04-12T22:11:00.0000000"   },   {     "Id":"3",     "CustomerId": "1",     "OrderName": "Order 3",     "Start": "2015-04-12T11:22:00.0000000",     "End": "2015-04-05T22:11:00.0000000"   } ]  

Расписание

var schedule = [   {     "CustomerId": "1",     "StartWeek": "2015-04-13T00:00:00.000Z",     "Schedule": [       {         "Id": "-1",         "Start": "2015-04-15T17:00:00+06:00",         "End": "2015-04-15T17:30:00+06:00",         "WorkType": "0"         "JobSpecificId": "-1",         "PaymentType": "-1",       },       {         "Id": "-1",         "Start": "2015-04-16T19:00:00+06:00",         "End": "2015-04-16T20:30:00+06:00",         "WorkType": "2",         "JobSpecificId": "-1",         "PaymentType": "-1"       },       {         "Id": "-1",         "Start": "2015-04-19T00:00:00+06:00",         "End": "2015-04-19T00:10:00+06:00",         "WorkType": "-1",         "JobSpecificId": "-1",         "PaymentType": "-1"       }     ]   },   {     "CustomerId": "2",     "StartWeek": "2015-04-13T00:00:00.000Z",     "Schedule": [       {         "Id": "-1",         "Start": "2015-04-13T00:00:00+06:00",         "End": "2015-04-13T00:00:00+06:00",         "WorkType": "-1",         "JobSpecificId": "-1",         "PaymentType": "-1"       }     ]   },   {     "CustomerId": "25",     "StartWeek": "2015-04-13T00:00:00.000Z",     "Schedule": [       {         "Id": "-1",         "Start": "2015-04-19T00:00:00+06:00",         "End": "2015-04-19T00:00:00+06:00",         "WorkType": "-1",         "JobSpecificId": "-1",         "PaymentType": "-1"       }     ]   } ]  

Есть список заказчиков, список заказов и расписание отправки. В календаре происходит редактирование расписания. То есть курьер может просматривать назначенные на него заказы. В расписание так же можно добавить/удалить курьера. Добавление или удаление происходит понедельно.

Реализация

Объект Store отвечает за хранение, получение и отправку данных на сервер. Реализация примерно такая:

var Store = function(){     var wrapSchedule= undefined;     …     this.loadScheduleList = function(){         ...         wrapSchedule = inMemory(serverResponse);     };     …     this.getScheduleList = function(){ return wrapSchedule };     …     this.saveScheduleList = function(){         var clientResponse= dumpAcc(wrapSchedule);         ...     }; } 

Функция InMemory как раз занимается тем, что приводит объект или массив, полученный с сервера в дерево указателей:

Функция inMemory

   function inMemory(from) {             return (function getNode() {                 var that = this;                  function facade(innerValue) {                     function facadeArray(_innerValue) {                         return (function(context) {                             return {                                 get: function() {                                     return context;                                 },                                 set: function(value) {                                     context = clone(value);                                 },                                 indexOf: function(index) {                                     return context[index];                                 },                                 push: function (v) {                                     context.push(getNode.call(clone(v)));                                 },                                 splice: function(index, count) {                                     context.splice(index, count);                                 },                                 orderBy: function (comparator) {                                     context.sort(comparator);                                 },                                 filter: function (predicate) {                                     return facadeArray(context.filter(predicate));                                 }                             }                         })(_innerValue);                     }                      function facadeObject(_innerValue) {                         return (function(context) {                             return {                                 get: function() {                                     return context;                                 },                                 set: function(value) {                                     context = clone(value);                                 },                                 update: function(obj) {                                     var keys = Object.keys(obj);                                     for (var prop_i = 0; prop_i < keys.length; prop_i++) {                                         if (context[keys[prop_i]]) {                                             context[keys[prop_i]] = clone(obj[keys[prop_i]]);                                         }                                     }                                 },                                 append: function(obj) {                                     var keys = Object.keys(obj);                                     for (var prop_i = 0; prop_i < keys.length; prop_i++) {                                         context[keys[prop_i]] = clone(obj[keys[prop_i]]);                                     }                                 }                             }                         })(_innerValue);                     }                      function facadePrimitive(_innerValue) {                         return (function(context) {                             return {                                 get: function() {                                     return context;                                 },                                 set: function(value) {                                     context = value;                                 }                             }                         })(_innerValue);                     }                      if (innerValue instanceof Array) {                         return facadeArray(innerValue);                     }                      if (isPrimitive(innerValue)) {                         return facadePrimitive(innerValue);                     }                      return facadeObject(innerValue);                 }                  if (that instanceof Array) {                     for (var i = 0; i < that.length; i++) {                         if (!isPrimitive(that[i]))                             that[i] = getNode.call(that[i]);                         else {                             that[i] = facade(that[i]);                         }                     }                 }                 if (!(that instanceof Array)) {                     for (var prop_i = 0; prop_i < Object.keys(that).length; prop_i++) {                         var field = that[Object.keys(that)[prop_i]];                         if (!isPrimitive(field)) {                             that[Object.keys(that)[prop_i]] = getNode.call(field);                         } else {                             that[Object.keys(that)[prop_i]] = facade(field);                         }                     }                 }                 that = facade(that);                 return that;             }).call(from);     }; 

Доступ к каждому уровню осуществляется через get, set или любую другую функцию узла. Например в facadeArray можно реализовать метод first, тогда получение расписания определенного курьера за определенную неделю выглядело бы так:

var firstOnWeekPredicate = function(x){     return x.get().CustomerId.get() == "1" && x.get().StartWeek.get() == '2015-04-13T00:00:00.000Z'; }  var scheduleByСourierIdAndWeek =  store.getScheduleList().get().first(firstOnWeekPredicate).get().Schedule 

Для тех, кто еще не знает про такие удобные функции как where, first, last, union, map и т.д., советую посетить underscorejs.org

Теперь во внутреннее значение (массив) scheduleByСourierIdAndWeek можно добавить новый день недели или изменить существующий.

Соответственно:

scheduleByСourierIdAndWeek.push({     "Id": "-1",     "Start": "2015-04-15T00:00:00+06:00",     "End": "2015-04-15T00:00:00+06:00",     "WorkType": "0"     "JobSpecificId": "-1",     "PaymentType": "-1" })  scheduleByСourierIdAndWeek.get()[0].update({WorkType: 2}) 

Весь «фасад» в функции getNode может быть переработан под собственные нужды.

Ну и что же мы получили в итоге? Мы разделили большой объект, полученный с сервера на связанные узлы. Раздали узлы в участки кода, где с ними происходит работа. Преобразовали данные так, как захотел пользователь. Теперь нужно все это собрать в объект и отправить обратно на сервер. Для этого есть функция dumpAcc.

   function dumpAcc(storeAcc) {         return clone((function getNodeValue(acc) {             if (typeof (acc.get ? acc.get() : acc) === "boolean"                 || typeof (acc.get ? acc.get() : acc) === "number"                 || typeof (acc.get ? acc.get() : acc) === "string") {                 return acc.get ? acc.get() : acc;             }             else if (acc.get() instanceof Array) {                 var _a = [];                 for (var _a_i = 0; _a_i < acc.get().length; _a_i++) {                     _a.push(getNodeValue(acc.get()[_a_i]));                 }                 return _a;             } else {                 var _o = {};                 for (var prop in acc.get()) {                     if (acc.get().hasOwnProperty(prop))                         _o[prop] = getNodeValue(acc.get()[prop]);                 }                 return _o;             }         })(storeAcc));     } 

То есть для того, чтобы получить clientResponse, нужно выполнить JSON.stringify(dumpAcc(store.getScheduleList())).
И напоследок функции clone, isPrimitive, которые я использовал.

    function clone(a) {         return JSON.parse(JSON.stringify(a));     }      function isPrimitive(value) {         return typeof (value) === "string" || typeof (value) === "number" || typeof (value) === "boolean";     } 

ссылка на оригинал статьи http://habrahabr.ru/post/255881/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *