Pull to refresh

Опыт создания Ajax-приложения

Reading time 8 min
Views 10K

В начале


В данной статье речь пойдет о написании Ajax-приложения. Если говорить проще — то, о написании сайта – работающего без перезагрузок. Быстро, легко, доступно. В этой статье не будет рассматриваться код серверной стороны, будут только примеры, для лучшего понимания.
Меня давно интриговала тема написания сайта, в котором несколько компонентов (например, flash плееры) не перезагружаются с каждым переходом по ссылкам, а продолжают себе напевать песенки. И вот однажды, набравшись смелости — я начал думать насчет структуры такого вот приложения. Что в итоге получилось — читайте ниже.

Начнемс


Для начала нам потребуется несколько js-плагинов – а именно:


Первый, это знакомый всем ну может почти всем — js фреймворк. Второй файл — это шаблонизатор, на базе jquery, мы будем его использовать — т.к. он работает на стороне клиента, что нам и нужно. Так же потребуется небольшое дополнение к jquery, для конвертации js-объектов в json.

Для начала создадим папку, например jstemp — в этой папке будут находится шаблоны jquery-tmpl. Еще нужно создать хотя бы один шаблон, например — шаблон главной страницы. Создаем в папке jstemp подпапку main в которой будет файл page.html
В файле простой html код:

<div id="name_of_page">${Content.name}</div>
<div id="info_of_page">${Content.info}</div>


Суть работы

В тот момент, когда пользователь заходит на страницу, наше ajax приложение обращается к серверу, с текущей ссылкой, и параметрами, и ждет в ответ JSON. Полученный JSON мы парсим в js-объект и отправляем плагину jquery-tmpl, который расставляет данные из объекта по полочкам, и показывает нужный шаблон нашему пользователю. А так же, если пользователь решит, что текущая страница ему не нужна, и попробует перейти на другую — ajax-приложение тут же отловит его действие и сделает те же самые процедуры, только уже с новой ссылкой, по которой юзер хотел перейти, и вернет результат работы.

Реализация


Для начала напишем начальную страницу:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script language="Javascript" src="js/jquery-1.5.min.js"></script>
        <script language="JavaScript" src="js/jquery.tmpl.js"></script> <!--Шаблонизатор-->  
        <script language="JavaScript" src="js/jquery.tmplPlus.min.js"></script> <!--Шаблонизатор-->  
       <script language="JavaScript" src="js/jquery.json-2.2.min.js"></script> <!-- $.toJSON -->  
        <title>Пример</title>
        <style type="text/css">
            body { font: 11pt Arial, Helvetica, sans-serif; margin: 0; }
            h1 { font-size: 36px; margin: 0; color: #fc6; }
            h2 { margin-top: 0; }
            #header { padding: 10px; }
            #sidebar { float: left; border: 1px solid #eee; width: 20%; padding: 5px; margin: 10px 10px 20px 5px; }
            #sidebar p { margin-left:20px; }
            #content { margin: 10px 5px 20px 25%; padding: 5px; border: 1px solid #eee; }
            #footer { background: #eee; padding: 5px; color: #fff; clear: left; }
            .error { padding:10px; width:80%;background-color:#ff4f4f; }
        </style>
    </head>
    <body>
        <div id="header"><h1>Какой - нибудь хеадер.</h1></div>
        <div id="sidebar">
            <h2>Ссылки</h2>
            <p><a href="#">Главная</a></p>
            <p><a href="#page/about">О сайте</a></p>
            <p><a href="#page/contacts">Контакты</a></p>
            <p><a href="#page/somepage">Какая - то страница</a></p>
            <p><a href="#user/login">Авторизация</a></p>
        </div>
        <div id="content">
            <h2>Какой - то контент </h2>
            <div id="content_wrap"></div><!-- Именно сюда будут грузиться данные -->
        </div>
        <div id="footer">© 1</div>
    </body>
</html>


Отметим наличие div с id= «content_wrap» — именно в него будут подгружаться загруженные шаблоны.

Далее напишем начальную загрузку контента.

var user = {};
/**
* Возвращает текущий hash без # в начале
**/
function hash() {
    return document.location.hash.replace('#','');
}

function load(u) {
    console.log('loading page '+ u +':');    
    $.ajax({
        url: "/" + u,//я обращаюсь на страницу, которую мне нужно загрузить, 
        //т.к. на серверной стороне - все запросы идут к index.php, 
        //и там я могу найти тот контроллер, который мне нужен
        type: "GET",
        data: "action=pageLoad&url="+u+"", // action=pageLoad на серверной стороне - говорит мне, что нужно возвращать JSON
        success: function(msg){//в этот момент, мы можем вернуть какие - то еще данные о юзере, из сервера, на текущий момент времени
            var arr = $.parseJSON(msg);
            console.log('success');            
            if (arr.my) {//в моем случае, я вернул в arr.my данные о пользователе
                //обновляем данные о юзере
                user = {
                    a: (arr.my.id)?true:false,
                    id:arr.my.id,
                    login: arr.my.login,
                    site_login: arr.my.site_login,
                    status: arr.my.status
                }

            }
            document.location.hash = u; 
            render(arr, u);
            console.log("load template");
        }
    });
}

function render(data, url) { //Грузит шаблон, параметр data - объект данных, передаваемых в шаблон; url - вида main/page , user/login 
    $("#content_wrap").show().html('');// обнуляем тот самый участок html-кода, в котором будет подгружена новая страница
    $.get("jstemp/"+ url +".html", function(template) {//получаем html файл - шаблона

        console.log("Template finded at jstemp/"+ url +".html");

        $.tmpl(template, {
            Content: data// в шаблоне tmpl все, переданные мною переменные будут хранится в массиве Content
        }).appendTo('#content_wrap');//добавляем к #content_wrap

        $("#content_wrap").prepend("<script language='JavaScript' src='js/included.js' />");//позже поясню для чего нужен этот код.

    });
}

$(function(){//функция, вызываемая после загрузки DOM
    if (hash()) {//если есть хэш
        var url = hash();// он и будет ссылкой, но без # в начале.
    } else {
        var url = 'main/page';// ставим какое - нибудь дефолтное значение
    }

    load(url);//загружаем страницу.
});


На данном этапе, если все нормально, то можно загрузить главную страницу. Для примера напишу совсем малюсенький серверный код:

<?php
if(isset($_GET['action'])&&$_GET['action']=='pageLoad'){
    switch($_GET['url']) {
        case 'page/contacts':
            $arr = array('name'=>'Контакты', 'info'=>'Связаться с нами можно с помощью сарафанного радио, ответ последует таким же образом.');
            break;

        case 'page/about':
            $arr = array('name'=>'О сайте', 'info'=>'Этот сайт написан не профессионалами за еду.');
            break;

        case 'page/somepage':
            $arr = array('name'=>'Еще одна страница', 'info'=>'Тут тоже что - то есть');
            break;

        case 'main/page':
        default:
            $arr = array('name'=>'Главная страница', 'info'=>'Разного рода информация');
            break;
    }

    echo json_encode($arr);
} else {
    ?>
    Тут Html код, который описан выше.
<?php } ?>


Теперь, при обращении к main/page — должно вывестись

Главная страница
Разного рода информация


Ура! 3/4 сделано. Теперь напишем простой скрипт, для отлова клики на ссылки.

/**
 * Клик на ссылке
 */
$("a[href^='#']").unbind('click');//старое событие нужно убрать, иначе появляется много одинаковых событий на один клик.
$("a[href^='#']").click(function(e){//селектор - только ссылки, у которых параметр href начинается с символа #
    var url = $("a:hover").attr('href').replace("#","");
    if (page_url != url) {
        if (url)
            load(url);
        else 
            load('main/page');
        return false;
    }
});


и поместим этот код в тот самый included.js
Назначение этого файла простое: При подгрузки html текста — скрипты, которые уже были на страницы, не хотят с ним работать, для этого приходит на помощь included.js.
А как же работа с формами, спросите вы? Да легко!

Работа с формами

Для примера создадим шаблон user/login.html
и поместим в него код:

{{if Content.error}}<!-- например сообщение о том, что пользователь уже залогинен --> 
<div class="error login">${Content.error}</div>
{{else}}
<form class="form"  id="login"  action="" method="POST">
    <div class="error" style="display:none;" id="message_login"></div><!--  Параметр id должен быть равен - message_[id_формы_выше] -->
    <table>
        <tr>	
            <td>Логин</td>
            <td><input name="username[login]" type="text" value="" /></td>
        </tr>
        <tr>
            <td>Пароль</td>
            <td><input type="password" name="username[password]" value="" /></td>
        </tr>
        <br />
        <tr>
            <td colspan="2"><input value="Войти" style="width: 300px; height: 70px;" type="submit" /></td>
        </tr>
    </table>
</form>
{{/if}}


А в тот же included.js следует добавить:

/**
 * сабмит формы
 */
$('form').submit(function() {//при отправке формы
    var form = $(this); 
    var method = form.attr('method'); //забираем method формы
    var id = form.attr('id'); //и id формы
    $.ajax({ //url: не нужен, т.е. мы отправляем запрос на текущую страницу.
        type: method,
        data: "ajax=1&"+form.serialize(), //отправляем серверу все данные формы в виде строки + параметр ajax - что должно указать, что возвращать нужно JSON
        success: function(msg){
            var arr = $.parseJSON(msg);
            if (arr.message) $("#message_"+id).show().html(arr.message);//если есть message(например ошибка), тогда вернем его в тот скрытый див.
            if (arr.go) { // если есть go - то должен быть совершен переход на другую страницу, в нашем случае - без перезагрузок страницы.
                if (arr.go == '' || arr.go == '/') arr.go = 'main/page';
                page.load(arr.go);//грузим другую страницу
            }
        }
    });
    return false;
});


Обновим серверный код, чтобы было понятно на примере, что происходит:

<?php
error_reporting(0);
session_start();
if(isset($_POST['username'])){//если отправили форму авторизации
    if(!$_SESSION['user']){//если еще не авторизированы
        $user = $_POST['username'];

        if($user['login']!='test'){ //если логин юзера не test, выдаем message
            echo json_encode(array('message'=>'Пользователя '.$user['login'].' не существует'));
        } else {//в противном случае перенаправляем юзера на главную страницу
            $_SESSION['user'] = $user['login']; //просто для примера.
            echo json_encode(array('go'=>'/'));
        }
    } else {//выдаем error если пользователь уже авторизирован
        echo json_encode(array('error'=>'Вы уже авторизированы.'));
    }
} else if(isset($_GET['action'])&&$_GET['action']=='pageLoad'){
    switch($_GET['url']) {
        case 'page/contacts':
            $arr = array('name'=>'Контакты', 'info'=>'Связаться с нами можно с помощью сарафанного радио, ответ последует таким же образом.');
            break;
        case 'page/about':
            $arr = array('name'=>'О сайте', 'info'=>'Этот сайт написан не профессионалами за еду.');
            break;
        case 'page/somepage':
            $arr = array('name'=>'Еще одна страница', 'info'=>'Тут тоже что - то есть');
            break;
        case 'main/page':
        default:
            $arr = array('name'=>'Главная страница', 'info'=>'Разного рода информация');
            break;
    }

    echo json_encode($arr);
} else {
    ?>
    тут полный html код
<? } ?>


При POST запросе со страницы, мы проверяем авторизирован ли юзер, и если да — выводим ошибку, и убираем форму, в противном случае — проверяем логин пользователя, если не существует — выводим Message, иначе — отправляем юзера на главную страницу.

В заключении


В заключении хочу сказать, что данный способ не самый правильный, и тем более идеальный, но он заслуживает место быть. Надеюсь, тема статьи была интересна, и может быть, даже, кому — то окажется полезной сама статья.

Спасибо за внимание.
Update: ссылка на сорцы
Tags:
Hubs:
+5
Comments 15
Comments Comments 15

Articles