В этом уроке мы модифицируем встроенную систему редактирования, которую создавали ранее. Также мы научим ее запоминать изменения.
Приготовление базы данных
Первым делом нам понадобится БД для хранения всей информации и для внесения изменений. Для наглядности давайте создадим таблицу в БД с произвольными данными.
Как Вы видите на картинке есть таблица data, и находится она в БД inplace. Автор урока решил создать еще одну таблицу.
Сделал он это с помощью вкладки SQL в PHPMYADMIN. Туда необходимо вставить следующий текст:
CREATE TABLE IF NOT EXISTS `inplace` (
`field` varchar(120) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`field`)
) ENGINE=MyISAM;
INSERT INTO `inplace` (`field`, `value`) VALUES
('name', 'am Siddharth'),
('passion', 'love working with the web'),
('profession', 'am a freelancer'),
('work', 'write for Net Tuts'),
('url', 'can be found at www.ssiddharth.com'),
('punch', 'will never let you down or give you up :)'),
('design', 'Get design approval from Yusuf'),
('invoice', 'Send an invoice to Drew'),
('research', 'Start research on Pallav\'s project'),
('discuss', 'Speak with Harnish about new ideas'),
('debug', 'Check Aditya\'s site for rendering bugs'),
('meet', 'Meet with Clintson to discuss new project');
После этого у нас получится следующее:
Взглянем на полученную таблицу:
Для простоты реализации и примера мы используем очень простую структуру таблицы. Продолжим работу над скриптом.
Создание файла подключения к БД
Поскольку мы будем использовать подключение к БД для внесения изменений, нам необходимо создать файл конфигурации подключения.
dp.php
<?php
DEFINE ('DB_USER', 'sid');
DEFINE ('DB_PASSWORD', 'somerandompassword');
DEFINE ('DB_HOST', 'localhost');
DEFINE ('DB_NAME', inplace);
$connection = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD) or
die('Connection to the specified database couldn\'t be established');
mysql_select_db(DB_NAME) or
die ('Specified database couldn\'t be selected');
?>
Тут ничего особенного. Вам лишь необходимо подставить свои данные.
Редактор
Редактор заботится о том, чтобы извлекать информацию из БД и выдавать пользователю в специальном формате, который нам позволит внести изменения и отправить эти изменения обратно в БД.
Код по сравнению с первой версией не содержит существенных семантических изменений. Но нам теперь необходимо сделать информацию динамической. Оригинальный HTML код:
<li class="editable">am Siddharth</li>
<li class="editable">love working with the web</li>
<li class="editable">am a freelancer</li>
<li class="editable">write for Net Tuts</li>
<li class="editable">can be found at <a href="http://www.ssiddharth.com">www.ssiddharth.com</a></li>
<li class="editable">will never let you down or give you up :)</li>
<li class="editable">Get design approval from Deacon</li>
<li class="editable">Send an invoice to Albert </li>
<li class="editable">Start work on Dwight's project</li>
<li class="editable">Talk with Sarah about new ideas</li>
<li class="editable">Check Seth's site for rendering bugs</li>
<li class="editable">Meet with Clintson to discuss project</li>
необходимо заменить на:
<?php
$query = "SELECT * FROM inplace LIMIT 0, 6";
$result = mysql_query($query) or die ('Query couldn\'t be executed');
while ($row = mysql_fetch_assoc($result)) {
echo '<li class="editable" id="'.$row['field'].'">'.$row['value'].'</li>';
}
?>
<?php
$query = "SELECT * FROM inplace LIMIT 6, 6";
$result = mysql_query($query) or die ('Query couldn\'t be executed');
while ($row = mysql_fetch_assoc($result)) {
echo '<li class="editable" id="'.$row['field'].'">'.$row['value'].'</li>';
}
?>
Поскольку таблица маленькая, мы выбираем все поля, но порциями по 6 штук. Далее проходим по всем полям и выводим их с помощью элементов li. Обратите внимание, что кроме содержания поля, мы также устанавливаем значение атрибута id на название поля откуда данные были взяты. Это будет использована далее, когда мы будем отправлять данные обратно на сервер.
Не забудьте в самом начале также подключить файл с подключением к БД.
<?php require("db.php"); ?>
После всех изменений не забудьте сохранить файл с расширением .php.
Обработчик
Обработчик проверяет была ли информация отправлена на страницу, и если была, проверяет отправленную информацию и вносит изменения где необходимо.
Создайте файл с названием handler.php и вставьте следующий код:
<?php
require("db.php");
if (isset($_POST['field']) && isset($_POST['value'])) {
$value = mysql_real_escape_string($_POST['value']);
$field = mysql_real_escape_string($_POST['field']);
$query = "UPDATE inplace SET value ='$value' WHERE field='$field'";
$result = mysql_query($query) or die ('Query couldn\'t be executed');
if ($result) {echo 1;}
}
?>
В самом начале подключаемся к БД.
Далее, проверяем были ли посланы обе наши (filed - поле, в которое необходимо внести изменения, value - значение изменения) обязательные переменные методом POST обработчику. Если да - можем продолжать работу. Если нет - ничего не происходит.
После проверки отправки этих переменных, мы можем готовить информацию для вставки в БД. Для простоты мы будем использовать функцию mysql_real_escape_string для очистки введенных данных от нежелательных символов. Если передавать без очистки - можно нарваться на риск SQL иинъекций.
После того, как мы убедились, что все безопасно, можем вносить изменения в необходимую запись. Все это делается с помощью простых SQL команд.
Если все идет по плану, выводится 1, которое подхватывает наш скрипт для определения результата операции.
JavaScript
Избавление от глобальных переменных (global variables)
Первоначальная версия содержала глобальные переменные для содержания данных, что приводило к неожиданным результат в определенных случаях. Исправим это вначале.
Самый простой способ исправить это - добавить спрятанное поле ввода рядом с оригинальным полем и использовать его в качестве буфера. Поскольку оно создается и разрушается на лету и принадлежит только одному элементу, мы можем менять\сохранять\отменять сколько угодно элементов сколько угодно раз без риска нарваться на ошибку.
Старую функцию replaceHTML необходимо заменить на:
function replaceHTML()
{
var buffer = $(this).html()
.replace(/"/g, """);
$(this).addClass("noPad")
.html("")
.html("<form class=\"editor\">
<input type=\"text\" name=\"value\" class=\"editBox\" value=\"" + buffer + "\"
/>
<input type=\"hidden\" name=\"buffer\" class=\"buffer\" value=\"" + buffer + "\"
/>
</form>
<a href=\"#\" class=\"btnSave\">Save changes</a>
<a href=\"#\" class=\"btnDiscard\">Discard changes</a>")
.unbind('dblclick', replaceHTML);
}
Совсем небольшое изменение. Для начала мы создаем внутреннюю переменную buffer для содержания оригинального значения. Далее мы очищаем HTML контент элемента родителя и вставляем наш собственный. Вдобавок к начальному сниппету, мы добавляем спрятанное текстовое поле, которое хранит изначальное значение. Больше изменений нет.
Создание унифицированного обработчика
Для начала мы унифицируем все функции для каждой ссылки:
function handler()
{
var selector;
if ($(this).hasClass("btnSave"))
{
selector = "editBox"
}
else
{
selector = "buffer"
}
$(this).parent()
.html($(this).siblings("form")
.children("."+selector)
.val())
.removeClass("noPad editHover")
.bind("dblclick", replaceHTML);
return false;
}
Вместо использования анонимных функций как прошлый раз, мы будем использовать нормальные функции. Мы изменим только небольшую часть функции, чтобы она могла обрабатывать запросы по сохранению и отмене.
Для начала мы задаем переменную selector, которая содержит селектор, который используется при изменении элементов li. editBox класс, который присваивается видимому текстовому полю и buffer - класс, который присваивается спрятанному текстовому полю, в котором находится первоначальное значение.
Поскольку мы унифицируем обработчики событий, нам необходимо проверить на какую ссылку нажали. Для начала смотрим была ли нажата ссылка с классом btnSave. Если да, тогда пользователь хочет сохранить изменения и поэтому мы присваиваем значение editBox к переменной selector. Если нет, присваивается buffer.
Остальная часть обработчика остается такой же, кроме того что селектор вставляется динамически в зависимости от действий, вместо того, чтобы быть прописанным заранее в коде.
Не забудьте обновить обработчики событий для каждой ссылки. Следующий код отвечает за это:
$(".btnSave, .btnDiscard").live("click", handler);
Добавление AJAX
- Подготовка HTML
Перед тем, как посылать информацию на сервер, нам необходимо найти способ отправлять важные детали обратно на сервер. В нашем случае нам необходимо 2 детали для осуществления удачного изменения.
- значение
- имя поля, которое необходимо изменить
Первая часть простая, поскольку у нас есть актуальное текстовое поле, в котором находятся значения для пересылки на сервер. Над второй частью необходимо немного поработать.
При создании редактора мы использовали главный ID таблицы в роли id атрибутов к каждому элементу li. Мы воспользуемся этим. Мы просто создадим еще одно спрятанное текстовое поле, которое будет содержать значение, которое в свою очередь будет отправлено назад на сервер.
function replaceHTML()
{
var buffer = $(this).html()
.replace(/"/g, """);
$(this).addClass("noPad")
.html("")
.html("<form class=\"editor\">
<input type=\"text\" name=\"value\" class=\"editBox\" value=\"" + buffer + "\"
/>
<input type=\"hidden\" name=\"buffer\" class=\"buffer\" value=\"" + buffer + "\"
/>
<input type=\"hidden\" name=\"field\" class=\"record\" value=\"" + $(this).attr("id")
+ "\" />
</form>
<a href=\"#\" class=\"btnSave\">Save changes</a>
<a href=\"#\" class=\"btnDiscard\">Discard changes</a>")
.unbind('dblclick', replaceHTML);
}
Функция replaceHTML необходимо изменить вот таким образом. Единственная разница - это добавление спрятанного текстового поля с именем field. Мы используем функцию jQuery attr для обращения к атрибуту ID элемента li и использования его в качестве значения для поля.
- Внедрение AJAX
Здесь мы воспользуемся стандартной функцией ajax.
function handler()
{
// Previous code
if ($(this).hasClass("btnSave"))
{
var selector = "editBox";
var str = $(this).siblings("form").serialize();
$.ajax({
type: "POST",
async: false,
timeout: 100,
url: "handler.php",
data: str,
success: function(msg){code = msg;}, });
if(code == 1)
{
alert ("Success");
}
else
{
alert ("Failure");
}
}
// Rest of the code
}
Поскольку нам необходимо отправлять информацию на сервер только в случае, когда пользователь нажал на соответствующую ссылку, мы обрамляем весь код в if блок, который мы создали ранее для проверки нажатия на элемент.
Я использую здесь функцию ajax, поскольку считаю ее самой быстрой. Для начала, сортирую данные от родительской формы для дальнейшей отправки на сервер. Далее, вызываю функцию ajax при этом указываю все детали (тип запроса - POST, и URL куда необходимо POST).
Внедрение строки состояния
Следующим нашим шагом является внедрение строки состояния процесса, чтобы показать результат работы скрипта.
Разметка
Тут нам не потребуется ничего особенного. Нам просто понадобится простой div, с которым мы можем работать. Мы добавим строку кода в наш редактор:
<div id="status"></div>
Обращаем внимание на id нашей строки. Это нам понадобится далее.
Функция помощи (helper)
В целях юзабилити мы создадим функцию помощи, которая при необходимости будет вносить изменения в строку состояния.
function UI(state)
{
var status = {};
status.Ready = "Ready";
status.Post = "Saving your data. Please wait...";
status.Success = "Success! Your edits have been saved.";
status.Failure = "Attempts to save data failed. Please retry.";
var background = {};
background.Ready = "#E8F3FF";
background.Post = "#FAD054";
background.Success = "#B6FF6C";
background.Failure = "#FF5353";
$("#status").animate({opacity: 0}, 200, function
(){$("#status").html(status[state]).css({background:
background[state]}).animate({opacity: 1}, 200)});
}
Функция под названием UI берет состояние строки состояния (сорри за каламбур) в качестве параметра. Внутри этой функции мы создаем два объекта: status содержит необходимый текст и background содержит цвет фона строки состояния.
Мы воспользуемся функцией jQuery animate для плавной анимации строки состояния. Для начала мы устанавливаем прозрачность на 0. Далее, мы меняем текст и фоновый цвет и делаем нормальную прозрачность.
Добавляем UI
Вместо всплывающих сообщений (как было в первой части) нам необходимо воспользоваться функцией UI.
Предыдущий блок после ajax запроса можно заменить на:
if(code == 1)
{
UI("Success");
}
else
{
UI("Failure");
}
Также не забудьте добавить UI("Ready"); при загрузке страницы, чтобы пользователь знал, что система готова для манипуляций и UI("Post"); - при отправки данных на сервер.
Правильное сохранение данных
И последнее, что нам необходимо сделать, это в случае сбоя при сохранении новых данных, нам необходимо не потерять изначальные данные. Нам необходимо убедиться, чтобы изначальный текст никуда не пропал до тех пора, данные успешно не обновлены.
Для этого нам необходимо изменить переменную selector в случае обнаружения ошибки.
if(code == 1)
{
UI("Success");
selector = "editBox";
}
else
{
UI("Failure");
selector = "buffer";
}
Если значение изменено успешно, мы меняем соответствующие значения переменных в editBox. Если же ошибка, нам необходимо подставить старые значения.
Заключение
Вот и все готово. Надеюсь, этот урок поможет Вам хоть немного разобраться в хитростях jQuery, a также создать свое интересное приложение. До следующих уроков!
Исходники:
здесь