Di Sole

Два примера создания data — таблицы на Dojo Toolkit и JQuery. Часть 1

Posted in Dojo Toolkit, JQuery, php by ignar on 15.04.2009

Для многих остается трудной задачей сделать правильную организацию учета данных в админчасти или просто красивый вывод, решил сделать для примера и наглядности на JQuery и Dojo Toolkit. Хочу заметить, что мне сильно импонирует dojo, потому попытаюсь как можно детальней все описывать, объяснять и провести аналогию в логике.

Это первая часть поста, с примером на jQuery, вторая часть на Dojo Toolkit.


Спешу сообщить, что мой адрес поменялся.
Если эта статья Вам интересна — тут она обновлена.

Живой блог лучше чем мертвый.

Новая rss волна — ignar’s log

Мой твиттер остался неизменным — ignart

Мои контакты


Статьи про Dojo Toolkit на DaddyFeed.com

Скачать рабочий пример можно тут

Функционал одинаковый для двух способов — добавление, редактирование, удаление и приятный внешний вид.

Для примера возьмем учет пользователей.

Структура и данные таблицы

CREATE TABLE users (
  id_user int(11) NOT NULL auto_increment,
  surname tinytext,
  patronymic tinytext,
  `name` tinytext,
  phone varchar(12) default NULL,
  email tinytext,
  url tinytext,
  `status` enum('active','passive','lock','gold') default 'active',
  `created` timestamp default current_timestamp,
  PRIMARY KEY  (id_user)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO users VALUES (1,'Иванов','Валерьевич','Александр','58-98-78','ivanov@email.ru',NULL,'active',now());
INSERT INTO users VALUES (2,'Лосев','Иванович','Сергей','9057777777','losev@email.ru',NULL,'passive',now());
INSERT INTO users VALUES (3,'Симдянов','Вячеславович','Игорь','9056666100','simdyanov@site.ru','http://www.site.ru/','active',now());
INSERT INTO users VALUES (4,'Кузнецов','Валерьевич','Максим',NULL,'kuznetsov@site.ru','http://www.site.ru','active',now());
INSERT INTO users VALUES (5,'Нехорошев','Юрьевич','Анатолий',NULL,NULL,NULL,'lock',now());
INSERT INTO users VALUES (6,'Корнеев','Александрович','Александр','89-78-36','korneev@domen.ru',NULL,'gold',now());

Структура приложения

Структура приложения

Структура приложения

Набросаем класс упрощающий выборку данных:

/php/database.php


class db{
	
	private $_host = 'localhost';
	private $_login = 'root';
	private $_password = 'root';
	private $_database = 'test';
	
	// в конструкторе коннектимся к БД
	public function __construct(){
		mysql_connect($this->_host, $this->_login, $this->_password);
		mysql_select_db($this->_database);
	}
	
	public function __destruct(){
		mysql_close();
	}
	
	public function sql_query($query){
		return mysql_query($query);
	}
	
	public function sql_fetch_object($query){
		$result = $this->sql_query($query);
		$rows = array();
		while ($row = mysql_fetch_object($result)){
			$rows[] = $row;
		}
		return $rows;
	}
	
	public function sql_fetch_array($query){
		$result = $this->sql_query($query);
		$rows = array();
		while ($row = mysql_fetch_assoc($result)){
			$rows[] = $row;
		}
		return $rows;
	}
	
	// вставляем не все данные, так как некторые 
	// могут быть пропущенны при валидации
	public function sql_insert_data($data){
		foreach ($data as $k=>$v){
			$strFields .= '`' . $k . '`,';
			$strValues .= "'$v',";
		}
		$strFields = substr($strFields,0,-1);
		$strValues = substr($strValues,0,-1);
		$this->sql_query("insert into users ($strFields) values ($strValues)");
		return mysql_insert_id();
	}
	
	public function sql_delete_by_id($ids){
		foreach ($ids as $id) {
			$str .= intval($id) . ',';
		}
		$str = substr($str,0,-1);
		$this->sql_query('delete from users where id_user in (' . $str . ')');
	}
	
	public function sql_save_by_id($data, $id){
		$query = "
			update users set 
				`surname` 	= '{$data['surname']}',
				`patronymic`= '{$data['patronymic']}',
				`name` 		= '{$data['name']}',
				`phone` 	= '{$data['phone']}',
				`email` 	= '{$data['email']}',
				`url` 		= '{$data['url']}',
				`status`	= '{$data['status']}',
				`created` 	= '{$data['created']}'
			where id_user = $id
		";
		echo $query;
		$this->sql_query($query);
	}
}
	

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

/php/template.php


class template {
	
	public function parseByData($data, $template){
		$str = $template;
		foreach ($data as $key=>$val){
			$str = preg_replace('/{'.$key.'}/', $val, $str);
		}
		return $str;
	}
	
	public function parceByTag($var, $tag, $template){
		return preg_replace("/\{$tag\}/", $var, $template);
	}
	
}

Сделаем шаблоны, которые послужат базой для примера. Разобьем всю страницу на шапку(header), часть шапки где будут подключаться скрипты(headerIncludes), шапка грида(grid_header), его нижняя часть(grid_footer), и шаблон с строкой(grid_body).

Подключение скриптов будет выглядеть следующим образом

// общие стили
<link type="text/css" rel="stylesheet" href="/css/index.css" />
// стили пользовательского интерфейса jQuery UI
<link type="text/css" href="/resources/smoothness/jquery-ui-1.7.1.custom.css" rel="Stylesheet" />
<script type="text/javascript" src="/lib/jquery/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/lib/jquery-ui/jquery-ui-1.7.1.custom.min.js"></script>
// скрипт с базовым кодом
<script type="text/javascript" src="/lib/init.jquery.js"></script>

Ряд таблицы, id строки совпадает с id записи в БД, а поля таблицы с классом div’a. Таким образом, всегда можно найти нужные нам данные.

<tr class="row_{id_user}" id="{id_user}" >
	<td><div class="id_user">{id_user}</div></td>
	<td><div class="surname">{surname}</div></td>
	<td><div class="patronymic">{patronymic}</div></td>
	<td><div class="name">{name}</div></td>
	<td><div class="phone">{phone}</div></td>
	<td><div class="email">{email}</div></td>
	<td><div class="url">{url}</div></td>
	<td><div class="status">{status}</div></td>
	<td><div class="created">{created}</div></td>
	<td><img class="edit" id="{id_user}" src="/img/edit.png" /></td>
</tr>

Шапка таблицы:

<thead>
	<tr>
		<th class="id"><div>id</div></th>
		<th class="sname"><div>Фамилия</div></th>
		<th class="pname"><div>Отчество</div></th>
		<th class="fname"><div>Имя</div></th>
		<th class="phone"><div>Телефон</div></th>
		<th class="email"><div>E-mail</div></th>
		<th class="url"><div>URL</div></th>
		<th class="status"><div>Статус</div></th>
		<th class="created"><div>Создан</div></th>
		<th class="action"></th>
	</tr>
</thead>
<caption><div class="caption">Данные пользователей</div><div id="indicator"></div></caption>

Нижняя часть грида:

<tfoot>
	<tr>
		<td colspan="10">
			<img src="/img/remove.png" class="delete" />
			<img src="/img/add.png" class="add" />
		</td>
	</tr>
</tfoot>

Индексный файл который будет все это компановать и выводить:

/index.php

require 'php/database.php';
require 'php/template.php';

$db = new db();
$db->sql_query('set names utf8');
$template = new template();

$data = $db->sql_fetch_array('select * from users');

$header = file_get_contents('templates/parts/header.html');
$headerIncludes = file_get_contents('templates/parts/headerIncludes.html');
$footer = file_get_contents('templates/parts/footer.html');
$grid_header = file_get_contents('templates/parts/grid_header.html');
$grid_body = file_get_contents('templates/parts/grid_body.html');
$grid_footer = file_get_contents('templates/parts/grid_footer.html');
$body = file_get_contents('templates/index.html');

foreach($data as $k=>$v){
	$htmlGrid .= $template->parseByData($v,$grid_body);
}

$content = $htmlGrid;

$html = $template->parceByTag($header,'header',$body);
$html = $template->parceByTag($grid_header,'grid_header',$html);
$html = $template->parceByTag($grid_footer,'grid_footer',$html);
$html = $template->parceByTag($headerIncludes,'headerIncludes',$html);
$html = $template->parceByTag($footer,'footer',$html);
$html = $template->parceByTag($content,'grid_data',$html);

header('Content-type: text/html; charset=utf-8');
echo $html;

А такеже скрипты для CRUD операций:

Добавления /insert.php


require 'php/database.php';
require 'php/template.php';

$db = new db();
$db->sql_query('set names utf8');

$template = new template();

$data['surname'] 	= htmlspecialchars($_POST['surname']);
$data['patronymic'] = htmlspecialchars($_POST['patronymic']);
$data['name'] 		= htmlspecialchars($_POST['name']);
$data['phone'] 		= htmlspecialchars($_POST['phone']);
$data['email'] 		= htmlspecialchars($_POST['email']);
$data['url'] 		= htmlspecialchars($_POST['url']);
$data['status'] 	= htmlspecialchars($_POST['status']);
$data['created'] 	= htmlspecialchars($_POST['created']);

$isDate = preg_match('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/',$data['created']);

$lastId = ($isDate == 1) ? $db->sql_insert_data($data) : 'not valid';

if ($lastId == 'not valid'){
	echo $lastId;
	die();
}

$grid_body = file_get_contents('templates/parts/grid_body.html');

$data['id_user'] = $lastId;
$html = $template->parseByData($data,$grid_body);

header('Content-type: text/html; charset=utf-8');
echo $html;

Редактирование /save.php


require 'php/database.php';
require 'php/template.php';

$db = new db();
$db->sql_query('set names utf8');

$id					= intval($_POST['id_user']);
$data['surname'] 	= htmlspecialchars($_POST['surname']);
$data['patronymic'] = htmlspecialchars($_POST['patronymic']);
$data['name'] 		= htmlspecialchars($_POST['name']);
$data['phone'] 		= htmlspecialchars($_POST['phone']);
$data['email'] 		= htmlspecialchars($_POST['email']);
$data['url'] 		= htmlspecialchars($_POST['url']);
$data['status'] 	= htmlspecialchars($_POST['status']);
$data['created'] 	= htmlspecialchars($_POST['created']);

$isId = ($id != 0) ? $id : false;
$isDate = preg_match('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/',$data['created']);

if ($isId && $isDate == 1) {
	$db->sql_save_by_id($data, $id);
}

echo 'ok';

Удаление /delete.php


require 'php/database.php';

$db = new db();
$db->sql_query('set names utf8');

$db->sql_delete_by_id($_GET['ids']);

echo 'ok';

Теперь, когда есть реализованный бекенд функционал, сначала реализуем интерфейс на jQuery, поместим весь функционал в один файл, назовем его init.jquery.js

Не думаю, что этот тот случай, когда стоит мудрить, сделаем все линейно и наглядно.

// выполнение любого функционала jQuery начинаем с ready
$(document).ready(function () {
	// текущий id редактируемой записи
	var editebleElementId;
	
	// массив выделенных строк
	var selectedRows = [];
	
	// функция которая будет разкрашивать через строчку наш грид
	// в конце скрипта будет ее вызов
	var evenGrid = function(){
		$('.grid tr:even').addClass('even');
	};
	
	// будем подсвечивать строку таблицы, на которую наведен курсор
	// выносим эту и предыдущую функции в отдельные, так как еще ни раз их вызовем
	var hoverGrid = function(){
		$('.grid tbody tr').hover(
			function(){
				$(this).addClass('hover');
			},
			function(){
				$(this).removeClass('hover');
			}
		);
	};
	
	// делаем запоминание выделенной строки, что б обойтись без чекбоксов
	// привязываем события с помощью live, это новая возможность jQuery 1.3
	// которая позволяет следить за всеми новодобавленными или измененными
	// элементами, которые подходят по требованию.
	// Если б мы использовали .click - событие не реагировало на 
	// новае элементы.	
	$('.grid tbody tr').live('click',
		function(){
			// получаем id строки по которой клацнули
			var id = parseInt(this.id);
			// смотрим есть ли такой элемент в массиве
			var index = selectedRows.indexOf(id); 
			if (index == -1) {
				// если нет - добавляем
				selectedRows.push(id);
			} else {
				// если есть удаляем
				selectedRows.splice(index, 1);
			}
			// добавляем или убираем класс выделения
			$(this).toggleClass('clicked');
		}
	);
	
	// для удобства 🙂
	var reinitGrid = function(){
		evenGrid();
		hoverGrid();
	};
	
	// будем сообщать для интерактивности, что происходит
	$("#indicator").ajaxStart(function(){
		$(this).html('В процессе').show();
		$('#loader').clone().attr('style','').appendTo(this);
	});
	$("#indicator").ajaxSuccess(function(evt, request, settings){
		 $(this).html("Выполнено");
	});
	$("#indicator").ajaxError(function(event, request, settings){
		$(this).html("Возникла ошибка при выполнении");
	});

	// функция редактирования
	$('.grid .edit').live('click',
		function(){
			// запоминаем что мы меняем
			editebleElementId = this.id;
			// собственно, сам диалог
			$("#dialog").dialog({
				height: 385,
				width: 400,
				modal: true,
				resizable: false,
				buttons: { 
					// кнопки диалога
					"Сохранить": function(){
						// объект который будет хранить измененные данные 
						var formData = {};
						// применяем к каждому текстовому полю функцию которая ...
						$("#dialogForm input").map(function(){
							// .. собирает хеш ( название элемента => его значение)
							// что то подобное асоциативному массиву php
							// напомню, что в javascript массивы не асоциативные
							formData[$(this).attr('name')] = $(this).val();
						});
						// передаем собранные данные
						$.post(
							// скрипту который все сохранит
							"save.php",
							// сами данные
							formData,
							// а так же, определим функцию которая сработает в случае
							// успешного запроса.
							// Напомню, что надо обязательно (что б не было проблем)
							// указать, какого типа данные ждать от сервера 
							// в нашем случае это простой текст
							function(data){
								// стандартный метод фреймворка, который перебирает
								// элементы массива или объекта
								jQuery.each(formData, function(i, val) {
									// в нашем случае заполняем каждый див ячейки, по очереди
									// измененными данными формы
									$('.row_' + editebleElementId + ' td div.' + i).html(val);
								});
							},
							"text"
						);
						// и удаляем диалог, в конце объясню зачем
						$(this).dialog("destroy"); 
					},
					"Отменить": function(){
						$(this).dialog("destroy"); 
					}
				},
				// при инициализации диалога
				open: function(event, ui) {
					// перебираем каждый див ячейки строки
					$('.row_' + editebleElementId + ' td div').slice(0,9).map(function(){
						// и заполняем форму диалога данными по соответствующим inupt'ам по его имени
					    valueName = $(this).attr('class');
					    $('#dialogForm #'+valueName).val($(this).html());
					});
				},
				close: function(event, ui) {
					$(this).dialog("destroy"); 
				}
			});
		}
	);
	
	// функция добавление, я уже не буду так расписывать, все по аналогии
	$('.grid .add').live('click',
		function(){
			// окрываем
			$("#dialog").dialog({
				height: 385,
				width: 400,
				modal: true,
				resizable: false,
				buttons: { 
					"Сохранить": function(){
						var formData = {};
						// собираем заполненные данные
						$("#dialogForm input").map(function(){
							formData[$(this).attr('name')] = $(this).val();
						});
						// отсылаем
						$.post(
							"insert.php",
							formData,
							function(data){
								if (data == 'not valid'){
									// если вдруг что то не так заполнено
									// в моем примере, мы строго не следили за валидностью
									// вводимых данных, но все же
									$("#indicator").html('Не верно заполненные данные');
								} else {
									// если все ок - находим последнюю строку грида
									// и вставляем после него новую строку, принятую с сервера
									$('.grid tbody tr:last').after(data);
									// заново окрашиваем грид через строчку
									reinitGrid();
								}
							},
							// не забываем указать, что ждем от сервера 
							"text"
						);
						// удаляем диалог
						$(this).dialog("destroy"); 
					},
					"Отменить": function(){
						$(this).dialog("destroy"); 
					}
				},
				open: function(event, ui){
					// если мы сначала, что то редактировали, то данные
					// так и остаются в форме, потому очищаем ее
					$('#dialogForm input').val('');
				},
				close: function(event, ui) {
					$(this).dialog("destroy"); 
				}
			});
		}
	);
	
	// удаление
	$('.grid .delete').live('click',
		function(){
			$("#confirm").dialog({
				height: 200,
				width: 400,
				modal: true,
				resizable: false,
				buttons: { 
					"Подтвердить": function(){
						// делаем запрос get протоколом
						$.get(
							"delete.php",
							{'ids[]':selectedRows},
							function(data){
								// при успешном ответе ищем все строки с собранными
								// id строк
								selectedRows.forEach(function(key,val){
									// и удаляем
									$('#' + key).remove();
									// не забываем удалить с массива выделенных элементов
									// ищем место где он находится
									var index = selectedRows.indexOf(key);
									// и удаляем
									selectedRows.splice(index, 1);
								});
								// заново разрисовуем таблицу
								evenGrid();
							}
						),'text';
						$(this).dialog("destroy"); 
					},
					"Отменить": function(){
						$(this).dialog("destroy"); 
					}
				},
				open: function(event, ui){
					// при открытии окна показываем сколько рядов выделено
					$('#confirm .dialogBody').html('Выделено: ' + selectedRows.length);
				},
				close: function(event, ui) {
					$(this).dialog("destroy"); 
				}
			});
		}
	);
	// как и говори в начале разрисовуем
	// и соеденяем события наведения мыши к таблице
	reinitGrid();
});

Пробуйте на работоспособность

Скачать рабочий пример можно тут

Выглядеть должно вот так:

грид jquery dojo toolkit

грид jquery dojo toolkit

Хочу еще раз напомнить, что это только первая часть.

Реклама
Tagged with: , ,

комментария 4

Subscribe to comments with RSS.

  1. […] Два примера создания data – таблицы на Dojo Toolkit и JQuery. Част… […]

  2. […] нашего приложения. Давайте назовем, по аналогии с первой частью, файл init.dojo.js и сохраним в […]

  3. […] примера создания data – таблицы на Dojo Toolkit и JQuery. Часть 1. Часть […]

  4. […] нашего приложения. Давайте назовем, по аналогии с первой частью, файл init.dojo.js и сохраним в […]


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

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: