Подходит к вам заказчик и говорит: хочу табличку с этим вот массивом данных о команде юзеров (показывает массив из трех записей). А этот вот по имени Петя, он среди них главный — так ниже и напишите. Стоимость: 30 y.e. Сроки: 1 час.
Вы: Ок. Нет проблем.
1. Ну, ту вы включаете все свои архитекторские скилы и начинаете: создаем структуру приложения, положили index.html, в папочку js закинули файлик app.js. Создали первую конструкцию:
(function() { var app = { config: { }, init: function() { } }; app.init(); })();
Гуд. Все работает. Круто.
Продолжим… Делаем паузу, и начинаем пытаться придумать как же нам нарисовать эту табличку с данными. Данные статические (так и хотел заказчик), поэтому нам не грозят всякие там «аяксы» и другие страшные вещи. Кстати вот и сами «некоторые данные».
someData: [ {name: 'Вася', surName: 'Шариков'}, {name: 'Саша', surName: 'Пупкин'}, {name: 'Петя', surName: 'Иванов'} ],
Ну раз статические, так и закинем их сразу в наше приложение. Ну, можем еще за раз создать и вызвать функцию создания таблички и подписи о тимлиде (да, вынесем сразу нашего Петю в конфиг).
(function() { var app = { config: { teamLeadName: 'Петя' }, someData: [ {name: 'Bob', surName: 'Smith'}, {name: 'Jack', surName: 'Smith'}, {name: 'Nick', surName: 'Smith'} ], init: function() { var app = this; return app; }, createTable: function() { var app = this; return app; }, createTeamLeadInfoBlock: function() { } }; app.init().createTable().createTeamLeadInfoBlock(); })();
На сколько сложной может быть вьюха строки юзера? Как часто она может изменятся? Как удобней было бы с ней работать? Да, вроде напрашивается использования шаблонизатора. Создадим наш шаблон для строки юзера в html файлике.
<script id="userTpl" type="text/template"> <tr> <td>{name}</td> <td>{surName}</td> </tr> </script>
В нашем случае это достаточно простой шаблон. Может кстати сейчас уже посмотреть на весь код файлика index.html, он уже не будет изменятся.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <table id="usersTable"></table> <div id="temLeadInfoBlock"></div> <script id="userTpl" type="text/template"> <tr> <td>{name}</td> <td>{surName}</td> </tr> </script> <script src="js/app.js"></script> </body> </html>
Вот и все. Весь наш штмл. Гуд.
Ну, что же, пора заюзать шаблон и что то вставить. Посмотрим как изменился наш код
(function() { var app = { config: { teamLeadName: 'Петя' }, someData: [ {name: 'Bob', surName: 'Smith'}, {name: 'Jack', surName: 'Smith'}, {name: 'Nick', surName: 'Smith'} ], init: function() { var app = this; app.utils = app.getUtils(); return app; }, createTable: function() { var app = this; app.utils.insertData('userTpl', app.someData, 'usersTable'); return app; }, createTeamLeadInfoBlock: function() { var app = this, utils = app.utils, parentView, data, tpl = '<p><span>Team lead - </span><b>{name} {surName}<b></p>', info; parentView = utils.getById('temLeadInfoBlock'); data = utils.getById(app.config.teamLeadName, app.someData, 'name'); info = utils.applyData(tpl, data); utils.setHtml(parentView, info); }, getUtils: function() { } }; app.init().createTable().createTeamLeadInfoBlock(); })();
Посмотрим что у нас тут появилось. Во первых
getUtils: function() { }
Как вы уже догадались она и будет делать основную часть работы. При реальной работе, данный функционал надо выносить в отдельный файл, но здесь я этого не стал делать, так как это и есть по сути наш основной код, но тем не менее написал её (getUtils) максимально гибкой для вынесения в другой файл и с контекстом не зависящим от нашего апликейшена.
Смотрим далее. В создании таблички видим один из методов нашей утилиты
app.utils.insertData('userTpl', app.someData, 'usersTable');
Хм, похоже на то, что это берет наш шаблон по идентификатору (userTpl), берет наши данные, и вставляет (наверное) в нашу таблицу (usersTable). Выглядит достаточно мило.
Идем далее, посмотрим теперь на создании блока с информацией о тимлиде
createTeamLeadInfoBlock: function() { var app = this, utils = app.utils, parentView, data, tpl = '<p><span>Team lead - </span><b>{name} {surName}<b></p>', info; parentView = utils.getById('temLeadInfoBlock'); data = utils.getById(app.config.teamLeadName, app.someData, 'name'); info = utils.applyData(tpl, data); utils.setHtml(parentView, info); },
Первое что бросается в глаза — это что здесь мы уже сами вручную создали шаблон (tpl), а не получаем его из нашего штмл. Это тоже бывает полезно.
Гуд. Следующая полезная функция
parentView = utils.getById('temLeadInfoBlock');
Не трудно догадаться, что оно вернуло нам ссылку на штмл-элемент по его идентификатору, но что мы видим далее —
data = utils.getById(app.config.teamLeadName, app.someData, 'name');
Хм… а это похоже на то, что мы получили обьект с массива, найдя его по заданному полю (name) когда оно будет равно app.config.teamLeadName (что как вы помните есть именем тимлида — Петя). Теперь у нас есть обьект с данными о тимлиде.
Ну и последние две строчки
info = utils.applyData(tpl, data); utils.setHtml(parentView, info);
Не трудно догадаться, что мы «насадили» данные на шаблон и записали их в DOM. Гуд. Осталось самое интересное, посмотреть что же дают нам наши утилиты
посмотрим что они из себя представляют:
getUtils: function() { var app = this, config = app.config; return { insertData: function(tplId, data, viewId) { var utils = this, tplView = utils.getHtmlView(tplId), tpl = tplView.innerHTML, prevHtml = '', resultHtml = ''; if(!utils.isArray(data)) { resultHtml = utils.applyData(tpl, data); } else { utils.forEach(data, function(item, index) { resultHtml += utils.applyData(tpl, item); }); } if (viewId) { utils.setHtml(viewId, resultHtml); } return resultHtml; }, applyData: function (tpl, obj) { var tplSymbols = config.tplSymbols, key; for (key in obj) { if (obj.hasOwnProperty(key)) { tpl = tpl.replace(new RegExp(tplSymbols[0] + key + tplSymbols[1], 'ig'), obj[key]); } } return tpl; }, setHtml: function(view, resultHtml) { var utils = this, prevHtml = ''; view = utils.getHtmlView(view); prevHtml = view.innerHTML; view.innerHTML = prevHtml + resultHtml; }, getHtmlView: function(view) { var utils = this, result; if (typeof view === 'string') { result = utils.getById(view); } else { result = view; } return result; }, isArray: function(arr) { return toString.call(arr) === '[object Array]'; }, forEach: function(arr, fn) { var i, max; for (i = 0, max = arr.length; i < max; i++) { fn(arr[i], i); } }, getById: function(id, arr, idProperty) { var utils = this, i; if (utils.isArray(arr)) { for (i = arr.length; i--;) { if (arr[i] && idProperty && arr[i][idProperty] == id) { return arr[i]; } } } else { return document.getElementById(id); } return null; } } }
И так. Пройдемся по функциям, с конца.
- getById: function(id, arr, idProperty) { — если передали только первый параметр, воспринимает его как идентификатор к штмл-элементу, ищет его в DOM. Часто бывает полезно получить какой то обьект с массива найдя его по какому то полю, что мы и сделали, передавая вторым параметром массив по которому искать и поле с которым сравнивать.
- forEach: function(arr, fn) { — не более чем «синтаксический сахар», который реализует перебор массива. В функцию обработки элемента массива, кроме самого элемента передаем еще значения итератора, это тоже часто бывает полезно знать
- isArray: function(arr) { — проверят является ли массивом входящий аргумент.
- getHtmlView: function(view) { — позволяет нам работать с штмл-элементами как напрямую, так и через идентификатор. Если входящий аргумент строка, интерпретируем его как идентификатор и ищем соответствующий штмл-элемент, возвращаем его.
- setHtml: function(view, resultHtml) { — дописываем штмл в переданный первым параметром родительский штмл-элемент.
- applyData: function (tpl, obj) { — «накладывает» обьект с данными на шаблону. Можно увидеть что, я вынес символы сигнализирующие о замене значений в шаблоне в конфиг аппликейшена (tplSymbols = config.tplSymbols), что делает наш шаблонизатор еще более гибким.
- insertData: function(tplId, data, viewId) { — вставляет сразу наш шаблон с данными в родительский элемент (viewId). Он, кстати, может не быть передан, функция возвращает нам сгенерированный штмл, который мы сможем вставить попозже. Также data может быть либо объектом, либо массивом объектов.
Вот и все. Посмотрим. Теперь на код всего аппликейшена.
(function() { var app = { config: { teamLeadName: 'Петя', tplSymbols: ['{','}'] }, someData: [ {name: 'Вася', surName: 'Шариков'}, {name: 'Саша', surName: 'Пупкин'}, {name: 'Петя', surName: 'Иванов'} ], init: function() { var app = this; app.utils = app.getUtils(); return app; }, createTable: function() { var app = this; app.utils.insertData('userTpl', app.someData, 'usersTable'); return app; }, createTeamLeadInfoBlock: function() { var app = this, utils = app.utils, parentView, data, tpl = '<p><span>Team lead - </span><b>{name} {surName}<b></p>', info; parentView = utils.getById('temLeadInfoBlock'); data = utils.getById(app.config.teamLeadName, app.someData, 'name'); info = utils.applyData(tpl, data); utils.setHtml(parentView, info); }, getUtils: function() { var app = this, config = app.config; return { insertData: function(tplId, data, viewId) { var utils = this, tplView = utils.getHtmlView(tplId), tpl = tplView.innerHTML, prevHtml = '', resultHtml = ''; if(!utils.isArray(data)) { resultHtml = utils.applyData(tpl, data); } else { utils.forEach(data, function(item, index) { resultHtml += utils.applyData(tpl, item); }); } if (viewId) { utils.setHtml(viewId, resultHtml); } return resultHtml; }, applyData: function (tpl, obj) { var tplSymbols = config.tplSymbols, key; for (key in obj) { if (obj.hasOwnProperty(key)) { tpl = tpl.replace(new RegExp(tplSymbols[0] + key + tplSymbols[1], 'ig'), obj[key]); } } return tpl; }, setHtml: function(view, resultHtml) { var utils = this, prevHtml = ''; view = utils.getHtmlView(view); prevHtml = view.innerHTML; view.innerHTML = prevHtml + resultHtml; }, getHtmlView: function(view) { var utils = this, result; if (typeof view === 'string') { result = utils.getById(view); } else { result = view; } return result; }, isArray: function(arr) { return toString.call(arr) === '[object Array]'; }, forEach: function(arr, fn) { var i, max; for (i = 0, max = arr.length; i < max; i++) { fn(arr[i], i); } }, getById: function(id, arr, idProperty) { var utils = this, i; if (utils.isArray(arr)) { for (i = arr.length; i--;) { if (arr[i] && idProperty && arr[i][idProperty] == id) { return arr[i]; } } } else { return document.getElementById(id); } return null; } } } }; app.init().createTable().createTeamLeadInfoBlock(); })();
Вот так мы в 150 строк сделали построения таблички. Тут конечно наша ленивая сторона, скажет
и предложит вам вот такое решение:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <table id="usersTable"></table> <div id="temLeadInfoBlock"></div> <script> (function() { var someData = [ {name: 'Вася', surName: 'Шариков'}, {name: 'Саша', surName: 'Пупкин'}, {name: 'Петя', surName: 'Иванов'} ], i, max, usersInfo = ''; for (i = 0, max = someData.length; i<max; i++) { usersInfo += '<tr><td>'+someData[i].name+'</td><td>'+someData[i].surName+'</td></tr>'; } document.getElementById('usersTable').innerHTML = usersInfo; document.getElementById('temLeadInfoBlock').innerHTML = '<p><span>Team lead - </span><b>'+someData[2].name +' '+someData[2].surName+'<b></p>'; })(); </script> </body> </html>
Хм… намного короче и проще, не так ли?
И того: что мы получили от «ленивой реализации» — сэкономили кучу времени. Или нет? Конечно, смотря какие задачи мы решаем. Но то что это не возможно в дальнейшем поддерживать и дописывать — это факт. Только если переписать все заново. Так сэкономило ли это нам время?
PS: этой статьей я хочу начать цикл статей по JavaScript, делая каждый раз средней сложности приложения, делая ударения на качество кода и архитектуру. Надеюсь в комментариях меня многие будут исправлять, и мы будем видеть много точек зрения и решений конкретных задач. Если же все очень плохо (в этой статье) — продолжать не буду.
PPS: убьем в себе кодера.!
ссылка на оригинал статьи http://habrahabr.ru/post/193522/
Добавить комментарий