Статьи

Создание системы редактирования на месте: шаг вперед

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

При всей шумихе вокруг Web 2.0, простота использования теперь намного важнее, чем когда-либо. Возможность редактировать какой-либо контент без перехода на другую страницу — это то, чего действительно жаждут многие пользователи. Многие громкие имена уже используют этот шаблон с большим эффектом. Если вы использовали Flickr, вы, вероятно, видели это в действии.

Сегодня мы собираемся улучшить более раннюю версию : устранить некоторые ошибки, добавить некоторые функции и, что более важно, сохранить все данные в фактической базе данных для сохранения. Заинтересованы? Давайте начнем прямо сейчас!

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

У меня уже была база данных с именем на месте с таблицей данных на моем сервере разработки. Для нашего использования мы добавим еще одну таблицу.

Учебное изображение

Я обычно предпочитаю использовать phpMyAdmin для выполнения моих SQL-запросов. Нажмите на вкладку SQL и вставьте следующий запрос:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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’);
Учебное изображение

Если все работает как надо, вы должны получить следующий экран:

Учебное изображение

Присмотритесь к столу:

Учебное изображение

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

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

Поскольку мы будем часто обращаться к базе данных либо для чтения данных, либо для обновления содержащихся в них данных, целесообразно создать файл конфигурации, который содержит соответствующие данные. Создайте файл с именем db.php и вставьте в него следующее.

01
02
03
04
05
06
07
08
09
10
11
<?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-кода только из более ранней версии. Однако нам необходимо сделать данные динамичными. Итак, в оригинальном HTML-коде это:

1
2
3
4
5
6
<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>
1
2
3
4
5
6
<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>

заменяется на:

1
2
3
4
5
6
7
<?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>’;
}
?>
1
2
3
4
5
6
7
<?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 . Обратите особое внимание на тот факт, что каждый элемент li получает свой атрибут id с именем поля, из которого он получает свое значение. Это будет использовано позже в данных, отправляемых обратно на сервер, чтобы указать, какая запись должна быть обновлена.

Я знаю, что разоблачение названия поля таким образом может представлять угрозу безопасности, но в правильно защищенной среде я не думаю, что это вызовет какие-либо проблемы. В противном случае вы можете просто использовать псевдонимы здесь и сделать обратный поиск на стороне сервера. Пусть ваши творческие соки текут там. Для очень прямой демонстрации это выглядело довольно излишним.

Также не забудьте включить в редактор файл db.php, который мы создали ранее. Эта линия позаботится об этом.

1
<?php require(«db.php»);

После внесения изменений не забудьте сохранить файл с расширением .php .

Обработчик — это место, где страница публикует подробности. Это позволяет проверить, действительно ли данные были отправлены на страницу, и, если да, очистить отправленные данные, а затем обновить соответствующие значения.

Создайте файл с именем handler.php и вставьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
<?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;}
}
?>

Довольно прямое дело. Позвольте мне объяснить каждый шаг в деталях.

Так как нам нужно будет манипулировать базой данных, мы сначала включаем файл db.php, который мы создали ранее.

Затем мы проверяем, отправляются ли наши обработчики как наши обязательные переменные, поле- значение, которое сообщает нам, какое поле нужно обновить, и значение — значение, которое нужно обновить. Если это так, мы можем приступить к реальной работе. Если нет, то ничего не происходит.

После того, как мы убедились, что переменные были отправлены, мы можем продолжить санацию данных для вставки в базу данных. Чтобы сделать его максимально простым, мы будем использовать функцию mysql_real_escape_string для очистки наших данных. Эта функция экранирует специальные символы, присутствующие в переданной строке. Если этот код передан без очистки, наш код подвергается атакам SQL-инъекций.

Теперь, когда мы убедились, что данные в безопасности, мы можем обновить соответствующую запись. Я предполагаю, что эта часть не нуждается в объяснении, поскольку это очень простой SQL. С точки зрения непрофессионала, в таблице размещения измените соответствующее значение поля на значение .

Если все идет по плану, верните значение 1, которое будет зафиксировано нашим сценарием, чтобы определить результат транзакции, чтобы она могла продолжаться соответствующим образом. Более подробно я расскажу ниже. Обратите внимание, что в этом случае я просто сообщаю, была ли попытка успешной или неудачной. В вашем проекте вы можете захотеть вернуть гораздо более подробную информацию в случае возникновения ошибки. Вы не ограничены моей чрезвычайно простой реализацией.

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

Одной из жалоб старой версии было повреждение данных, когда определенные действия выполнялись в определенном порядке. Это было связано с моей крайней потребностью в простоте и краткости, что в конечном итоге привело меня к тому, что я упустил этот конкретный сценарий. Тем не менее, мы исправим это сегодня.

Я предполагаю, что у вас есть старый код JavaScript для сравнения и редактирования.

Первая версия использовала глобальные переменные для хранения исходных данных, что в некоторых случаях приводило к неожиданным результатам. Мы исправим это в первую очередь.

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

Старая функция replaceHTML обновляется до:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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 родительского элемента и добавляем наш собственный. В дополнение к исходному фрагменту мы добавляем скрытое текстовое поле, в котором сохраняется исходное значение. Здесь больше ничего не меняется.

Предыдущая итерация связывала похожие, но отдельные функции для каждой из функциональных ссылок. Мы объединим их здесь.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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 переменной селектора . Если нет, буфер назначен.

Остальная часть обработчика остается той же, что и в старой версии, за исключением того, что селектор вводится динамически в зависимости от действия, а не жестко кодируется в функцию. Если вы, кажется, потерялись здесь, посмотрите на первую часть серии, чтобы понять, что делает последний блок. По сути, мы вставляем значение выбранного текстового поля в родительский элемент li и перепривязываем исходный обработчик событий.

Не забудьте обновить обработчики событий для каждой ссылки. Об этом позаботится следующий лайнер:

1
$(«.btnSave, .btnDiscard»).live(«click», handler);

Если вам интересно, почему я использовал здесь функцию live , обратитесь к предыдущей статье.

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

Прежде чем мы сможем отправить данные на сервер, нам нужно найти способ отправить соответствующие данные обратно на сервер. В этом случае нам нужно 2 детали для успешного редактирования.

  • Само значение
  • Название поля для обновления

Первая часть довольно проста, так как у нас есть фактическое текстовое поле, содержащее значения для отправки на сервер. Вторая часть требует небольшой работы.

При создании редактора помните, что мы использовали первичный идентификатор таблицы в качестве атрибутов идентификатора для каждого элемента li ? Мы собираемся использовать это здесь. Мы просто создадим еще одно скрытое текстовое поле, в котором будет храниться значение, которое затем можно будет отправить обратно на сервер.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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 должна быть обновлена ​​следующим образом. Единственное отличие заключается в добавлении скрытого текстового поля с полем имени. Мы используем функцию attr jQuery для доступа к атрибуту ID элемента li и используем его в качестве значения текстового поля.

Тогда перейдем к реализации AJAX. Мы собираемся использовать стандартную функцию jQuery ajax здесь.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 для публикации. Мы также указываем, что данные, которые мы сериализовали ранее, должны быть отправлены на сервер.

Обычно для дальнейшего внесения изменений вы используете встроенные обратные вызовы success и error, но я решил не делать этого здесь. Вместо этого я просто фиксирую текст, отправленный обратно сервером. Если он возвращает 1, то значение, которое мы настроили для обработки нашего обработчика, если все произошло правильно, мы предупреждаем пользователя, чтобы он знал.

Учебное изображение

Оповещения являются довольно элементарным способом обновления пользователя со статусом действия. Имея это в виду, мы собираемся отказаться от системы оповещений и вместо этого реализовать простую строку состояния внизу, которая отражает эти изменения.

Нам здесь не нужно ничего особенного. Нам просто нужен простой элемент div, которым мы можем манипулировать. Нам просто нужно добавить это прямо в редакторе.

1
<div id=»status»></div>

Запишите атрибут id . Мы будем использовать это позже.

В целях повторного использования кода мы создадим вспомогательную функцию, которая обновляет строку состояния по мере необходимости.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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 , принимает состояние строки состояния в качестве параметра. Внутри функции мы создаем два объекта: статус содержит соответствующий текст, а фон — цвета фона строки состояния.

Мы могли бы просто обновить текст строки состояния и цвет фона, но здесь, в Net Tuts, мы не катимся. 🙂

Мы собираемся использовать функцию анимации jQuery для изящной анимации строки состояния. Сначала мы оживляем его непрозрачность до нуля. Затем мы обновляем его цвет текста и фона, а затем возвращаем его к полной видимости.

Обратите особое внимание на тот факт, что логика, используемая для обновления значений, заключена в анонимную функцию и передана как обратный вызов исходной анимации. Таким образом, панель будет анимирована до нуля, а затем все обновится. Если анимации связаны друг с другом, цвета текста и фона будут обновлены сразу после запуска начальной анимации, что приведет к очень резкому эффекту.

Добавление его в пользовательский интерфейс и обновление строки состояния теперь очень просто. Вместо предупреждений, которые мы использовали ранее, нам нужно использовать функцию пользовательского интерфейса .

Предыдущий блок, который завершился вызовом ajax, теперь может быть заменен на:

1
2
3
4
5
6
7
8
if(code == 1)
{
    UI(«Success»);
}
else
{
    UI(«Failure»);
}

Также не забудьте добавить пользовательский интерфейс («Готов»); когда страница загружается, чтобы пользователь знал, что система готова к манипуляциям и пользовательскому интерфейсу («Опубликовать»); когда данные публикуются на сервере.

Когда вы добавляете свои собственные состояния на панель задач, обратите особое внимание на тот факт, что строка, которую мы отправляем в качестве параметра функции, отображается непосредственно в свойство объекта.

Последнее, на что нам нужно обратить внимание, это тот факт, что если попытка сохранить данные не удалась, обновленный текст все еще сохраняется. Это кажется довольно противоречивым. Если попытка сохранить данные не удалась, мы должны убедиться, что исходный текст помещен обратно, чтобы пользователь знал, что данные не были сохранены.

Чтобы исправить это, нам нужно изменить переменную селектора в случае возникновения ошибки.

01
02
03
04
05
06
07
08
09
10
if(code == 1)
{
    UI(«Success»);
    selector = «editBox»;
}
else
{
    UI(«Failure»);
    selector = «buffer»;
}

Если значение было успешно отредактировано, мы меняем значение соответствующей переменной на editBox . Но если попытка закончилась неудачей, нам нужно заменить новое значение на старое. Поэтому мы назначаем буфер переменной, чтобы значение вернулось к исходному значению.

И там у вас есть это. Как добавить удобную функциональность в ваши проекты. Надеюсь, вы нашли этот урок интересным, и он был вам полезен. Не стесняйтесь многократно использовать этот код в других местах своих проектов и звоните сюда, если у вас возникнут трудности.

Пожалуйста, имейте в виду, что эта система была разработана с основной целью обучения методам, связанным с этим, а не как производственная система, предназначенная для внедрения в существующие системы. Это больше основа, на которой я призываю людей развивать и совершенствовать.

Вопросов? Хорошие вещи, чтобы сказать? Критицизмы? Нажмите на раздел комментариев и оставьте мне комментарий. Удачного кодирования!

  • Подпишитесь на нас в Твиттере или подпишитесь на ленту Nettuts + RSS для получения лучших учебных материалов по веб-разработке. готов

Знаете ли вы, что вы можете заработать до 600 долларов за написание учебника PLUS и / или скринкаст для нас? Мы ищем подробные и хорошо написанные учебники по HTML, CSS, PHP и JavaScript. Если у вас есть такая возможность, пожалуйста, свяжитесь с Джеффри по адресу [email protected].

Обратите внимание, что фактическая компенсация будет зависеть от качества окончательного урока и скринкаста.

Написать ПЛЮС учебник