Pull to refresh

Привязка модели данных к представлению на javascript

JavaScript *
Я думаю многие пользуются DataBinding`ом в .NET, она же привязка данных. Она позволяет, при выделенной модели данных и независимом визуальном представлении, забыть о синхронизации данных с формой. Привязав один раз properties модели к элементам представления, мы избавляемся от десятков обработчиков onChange и подобных для изменения полей модели, а в случае изменения поля вне gui-интерфейса (например синхронизация модели с базой), мы не будем мешать уровни абстрации, чтобы обновить textBox.text. Давайте данные будут отдельно, gui отдельно. Плюс мы получаем наглядный, легко переносимый код на asp.net или даже на другой язык/платформу.

 databinding

Синхронизация может быть как в двух направлениях, так и в одном, причем в любую сторону.
С развитием уровня web-ориентированных приложений, появилась возможность реализации подобного решения и на JavaScript. Ну думаю преимущества разделения данных от представления уже много где рассматривались и не раз приводили к холивару, так что упустим этот момент. Примеры буду рассматривать на jQuery Например, при классическом хранении данных в DOM структуре, сохранение сотки галочек в какой-то анкете (formBody) можно реализовать таким способом:
$('table#formBody input[name="available"][change=true]')

Флаг «change» используем для оптимизации выборки, т.е выбираем только измененные элементы А каждая галочка содержит теги idRef — id элемента, доступность которого она отражает Теперь, выбрав все элементы, перебираем их и переносим в хеш-массив
var find = {skills: []};
$j('table#formBody input[name="available"][change=true]').each(
  function(index, el){
   find.skills.push({ id: $j(el).attr('idRef'), val: ($j(el).attr('checked') ? 1 : 0) });  
  }
);


На родном функционале Javascript код будет достаточно громоздок, приводить его не буду, но суть не меняется.
Минусы очевидны, данные очень размазаны по всему DOM, редактирование логики модели или формы представления зачастую могут привести к ошибкам, т.к. сложно все упомнить, за всем уследить, тем более если готовый html приходит из php, придется править два файла, что также может привести к ошибкам. В поисках какого-то готового решения привязки данных на JavaScript, я обнаружил, что решений много, например только среди plug-in`ов jQuery я нашел jPop, JQBinder, Databind plug-in, jBind, JSRepeater, Chain.
Рассмотрим по порядку.

jPop


Особенности:
  • поддержка массивов.
  • однонаправленная синхронизация от представления к модели.
  • нельзя выбрать поле тега, к которому мы хотели бы привязать поле объекта, это либо
  • в качестве источника данных используется локальная модель.
  • нет документации
Принцип работы достаточно прост, Имя ключа (key) модели сопоставляется в id тега в DOM структуре и заполняется значением, вот небольшой пример, показывающий функционал плагина:
<h1 id="greeting" class="jpop_class"></h1>
<p id="greetingProp.greeting" class="jpop_class"></p>
<p>And,
<span id="greetingProp2:greetingArray" class="jpop_class">
<br><span id="greetingArray.greeting" class="jpop_class"></span>
</span>
<script type="text/javascript">
$j(function() {
  var jso= {
    greeting: "Hello, World!",
    greetingProp: {
      greeting: "Hello again, World!"
    },
    greetingProp2: {
      greetingArray: [
        { greeting: "Hello, Nick!" },        
        { greeting: "Hello, Sri!" },
        { greeting: "Hello, Jody!" },
        { greeting: "Hello, Andrew!" },
        { greeting: "Hello, Heather!" },
        { greeting: "Hello, Ben!" }
      ]
    }
  };
  $j('.jpop_class').jpop(jso);
});


JQBinder


Особенности:
  • нет нормальных примеров
  • нет описания
  • нет документации
  • нет синхронизации
Пример:
<div id="#target">
<img src='$(.profile_image_url)' />
<div>$(.text)</div>
</div>

Then, simply call
$("#stage").dataBindTo( { urlToJSONP, options } );

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

Databind plug-in


Особенности:
  • имеется карта привязки модели к представлению
  • нет синхронизации
  • в качестве источника данных и карты привязки используется локальная модель.
  • нет документации, но есть много примеров
Библиотека представляет из себя шаблонизатор.
Пример:
<form action="#" id="form1">
    <label for="txtName">Name</label><input type="text" id="txtName"><br>
    <label for="txtAge">Age</label><input type="text" id="txtAge"><br>
    <label for="drpGender">Gender</label><select id="drpGender"><option value="0">Female</option><option value="1">Male</option></select>
</form>

var map = { name: '#txtName', age: '#txtAge', gender: '#drpGender', genders: '#drpGender' };
var data = { name: 'Jane Doe', age: 27, genders: [[0, 'Female'], [1, 'Male']], gender: 0 };

$('#form1').binddata(data, map); //Bind with a map

jBind


Особенности:
  • в качестве источника данных и карты привязки используется локальная модель.
  • нет синхронизации
Библиотека представляет из себя шаблонизатор.
Пример:
<div id='template2' style="display:none;">
  <div class="content">
    <!--data-->
      <div class="viewBlockLeft" id='{id}'>
        #{id}<br/>
        <b>{name} {family}</b>,<br/>
        <i>{education}</i>,<br/>
      </div>
    <!--data-->
    <div class="clear"></div>
  </div>
</div>

var template = $('#template2').html();
var data = [
{
    id:41,
    name:'Scott',
    family:'Adams',
    education:'Economics'
},
{
    id:59,
    name:'Jack',
    family:'Welch',
    education:'Chemical Engineering'
}
];
alert($(template).bindTo(data));

JSRepeater 


Особенности:
  • возможность форматирования данных.
  • поддержка массивов и деревьев.
  • в качестве источника данных используется локальная модель.
  • поддержка встроенных переменных.
  • отсутствует синхронизация.
Библиотека представляет из себя шаблонизатор.
Пример использования:
<script>
var myData = {"Name" : "Google", "Type" : "Search Engine", "URL" : "www.google.com"};
$('#template1').fillTemplate(myData);
</script>
<body>
<div id='template1'>${Name} is a ${Type} found at <a href='${URL}'>${URL}</a></div>
</body>

Chain


Особенности:
  • хорошая документация.
  • возможность синхронизации с моделью через события, плюс возможность управлять процессом синхронизации.
  • поиск данных по модели.
  • возможность форматирования данных.
  • реализации drag&drop представления
  • очень много примеров
Очень мощный по возможностям builder html кода по шаблону, с возможностью синхронизации с моделью. Описать его функционал не получится, это тема отдельной статьи, а может и книги.
Пример:
$('<div><div class="name"><span class="first">First</span> <span class="last">Last</span></div></div>')
  .item({first:'Steve', last:'Jobs'})
  .chain({
    '.name .first': {
      style: 'color: blue;',
      content: 'First Name: {first}'
    },
    '.name .last': 'Family Name: {last}'
  })
  .appendTo(document.body);


Вот несколько примеров генерации таблиц http://javascriptly.com/2008/08/a-better-javascript-template-engine/
Еще пример:
<div id="contact">
  <div class="name">My Name</div>
  <div class="address">My Address</div>
  <div class="country">Unknown Country</div>
</div>

$('#contact').item({
    name: "Rizqi Ahmad",
    address: "Somestrasse 50, Hamburg",
    country: "Germany"
}).chain({
    '.name': 'Hello, My Name is {name}',
    '.address': 'My address is {address}',
    '.country': 'It is in {country}'
});

В результате имеем:
<div id="contact">
  <div class="name">Hello, My Name is Rizqi Ahmad</div>
  <div class="address">My address is Somestrasse 50, Hamburg</div>
  <div class="country">It is in Germany</div>
</div>

Подведем итоги, если нам нужна просто привязка с синхронизацией, стоит обратить внимание на jPop и немного его доработать. Если представление не ограничивается просто input`ами, а предполагает работу с динамическими табличками, списками, деревьями, с возможностями сортировки, drag&drop`а, то тут стоит всерьез изучить возможности библиотек от raid-ox. jQuery + Chain + Interaction очень может упростить жизнь JavaScript программисту для проектирования компактным и легкочитаемых web-приложений.
Удачного программирования!
Tags:
Hubs:
Total votes 39: ↑33 and ↓6 +27
Views 2.8K
Comments 9
Comments Comments 9