Toto je 3. díl seriálu jQuery: Praktická webová aplikace, pokud se dostatečně neorientujete, zkuste předchozí díl či díl první.
Toto je 3. díl seriálu jQuery: Praktická webová aplikace, pokud se dostatečně neorientujete, zkuste předchozí díl či díl první.
V minulých dílech už jsme zvládli udělat, aby naše webová aplikace byla schopna přepínat mezi jejími stránkami s krásným efektem. Je na čase dát jí nějakou užitečnou funkcionalitu. Začneme přidáváním příspěvků.
Cílem tohoto seriálu není zabývat se serverovou částí naší aplikace, nicméně pro úplnou představu a možnost si aplikaci prakticky vyzkoušet je nezbytné, abychom si řekli, jak bude fungovat serverová strana. Vycházíme z toho, že na našem serveru, jako na většině webových serverů, běží PHP a MySQL.
Naše MySQL databáze bude obsahovat pouze jednu tabulku a to tabulku příspěvků, pojmenujme ji třeba posts a dejme ji 4 sloupečky: ID, autor, text a čas vytvoření. Stačí na MySQL serveru vybrat vaši nově vytvořenou databázi pro účely naší webové aplikace a provézt v ní následující SQL kód, který se postará o tvorbu tabulky:
CREATE TABLE `posts` (
`id` int(11) NOT NULL,
`text` text NOT NULL,
`author` varchar(30) NOT NULL,
`created` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
ALTER TABLE `posts` ADD PRIMARY KEY (`id`);
ALTER TABLE `posts` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
Dále si vytvoříme PHP skript ve stylu REST API, který bude odpovídat na AJAX dotazy naší webové aplikace. Tento skript bude obstarávat 3 typy dotazů:
Následující PHP kód si uložíme do souboru, který pojmenujeme index.php. Je velmi jednoduchý a bude fungovat jako naše REST API. Přidáme k němu ještě soubor s názvem .htaccess s ještě níže uvedeným obsahem. Oba soubory si je nahrajte na váš Apache web server a nezapomeňte v index.php nastavit správné přístupové údaje k MySQL databázi.
<?phpini_set('display_errors', 1);ini_set('error_reporting', 'E_ALL');header('Content-Type: application/json');header('Access-Control-Allow-Origin: *');// Change DB access credentials here$dbConnection = new MySQLi('localhost', 'your user', 'your password', 'your database name');$dbConnection->set_charset('utf8');//// main switch, every request is then handled by individual functionsswitch($_REQUEST['action']){case 'posts':if($_SERVER['REQUEST_METHOD'] == 'POST'){if(isset($_REQUEST['text']) && isset($_REQUEST['author']))addPost($_REQUEST['text'], $_REQUEST['author']);elseerror(422, "Both 'text' and 'author' parameters are required.");}if($_SERVER['REQUEST_METHOD'] == 'GET'){if(isset($_REQUEST['id']))getPost($_REQUEST['id']);elselistLatestPosts(30);}break;default:error(400, 'Unknown action.');break;}// helper output functionfunction output($response){http_response_code(200);print json_encode($response);}// helper error functionfunction error($status, $message){http_response_code($status);print json_encode(array('message' => $message));}// function handling adding postsfunction addPost($text, $author){global $dbConnection;$text = $dbConnection->real_escape_string($text);$author = $dbConnection->real_escape_string($author);$sql = "INSERT INTO `webdevwiki`.`posts` (`id`, `text`, `author`, `created`) VALUES(NULL, '{$text}', '{$author}', UNIX_TIMESTAMP());";$result = $dbConnection->query($sql);if($result)output('');elseerror(500, $dbConnection->error);}// function returning latest postsfunction listLatestPosts($amount){global $dbConnection;$amount = $dbConnection->real_escape_string($amount);$sql = "SELECT * FROM `posts` ORDER BY `created` DESC LIMIT {$amount}";$result = $dbConnection->query($sql);if($result){if($result->num_rows > 0){$posts = array();while(($post = $result->fetch_object()) != null){$post = array('id' => $post->id,'text' => substr($post->text, 0, 100).'...','author' => $post->author,'created' => $post->created);$posts[] = $post;}output($posts);}elseerror(404, 'There are no posts.');}elseerror(500, $dbConnection->error);}// function returning a post's detailfunction getPost($id){global $dbConnection;$id = $dbConnection->real_escape_string($id);$sql = "SELECT * FROM `posts` WHERE `id` = {$id}";$result = $dbConnection->query($sql);if($result){if($result->num_rows > 0){$post = $result->fetch_object();$post = array('id' => $post->id,'text' => $post->text,'author' => $post->author,'created' => $post->created);output($post);}elseerror(404, "This post({$id}) does not exist.");}elseerror(500, $dbConnection->error);}
Soubor .htaccess bude obsahovat:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ([^\/]+$) index.php?action=$1 [QSA,L]
</IfModule>
Řekněme, že jste si oba zmíněné soubory nahráli do vašeho webového serveru, například na adresu http://vašedoména/api/
nebo http://localhost/jqueryapp/
. O této vaší adrese se nyní budeme bavit jako o adrese /
, ale budeme mít vždy na mysli celou adresu, tedy například /posts
by u vás mohlo být například http://localhost/jqueryapp/posts
.
Náš předpřipravený REST API skript nám teď dává k dispozici 3 operace:
/posts
, server nám odpoví seznamem posledních příspěvků a jejich texty budou zkráceny na 100 znaků./posts
, server nám odpoví detailem jednoho příspěvku s kompletním textem./posts
, server přidá do databáze nový příspěvek.V budoucnu bychom mohli samozřejmě do našeho REST API serveru přidávat další aspekty naší aplikace. Pokud bychom například chtěli pracovat s uživateli, vyhradili bychom si pro ten účel adresu /users
nebo pokud bychom chtěli mít u příspěvků tagy/štítky, bylo by vhodné s nimi pracovat na adrese /tags
. Ale nyní už máme vše připravené k tomu, abychom v naší webové aplikaci mohli implementovat přidávání příspěvků.
V souboru index.html si najdeme <div>
určený pro stránku přidávání příspěvku (#page-add
) a změníme jeho obsah tak, aby vypadal následovně:
<div id="page-add" class="page"><div class="col-xs-12" style="text-align: center;"><h1>What's on your mind?</h1></div><div class="col-xs-12 col-md-6"><label for="add-text">Your thoughts</label><textarea id="add-text" class="form-control" rows="5"></textarea></div><div class="col-xs-12 col-md-6"><label for="add-author">Your nickname</label><input id="add-author" type="text" class="form-control" maxlength="30"></div><div class="col-xs-12" style="text-align: center; margin: 24px;"><button class="btn btn-primary" onclick="app.add.post()">Share</button></div></div>
Máme zde opět několik Bootstrap CSS tříd, které nám značně usnadňují práci a zkrášlují naši aplikaci:
col-xs-12
- na malých obrazovkách (na mobilech) roztáhne daný element (a jeho obsah) na celou šířku.
col-xs-12 col-md-6
- na malých obrazovkách (na mobilech) roztáhne daný element (a jeho obsah) na celou šířku, na středně velkých obrazovkách a větích na polovinu šířky obrazovky.
form-control
- zkrášlí a zlepší vstupní pole
btn btn-primary
- zkrášlí tlačítko
Pokud nyní v naší webové aplikaci v menu liště klikneme na odkaz Add, zobrazí se nám stránka s formulářem pro přidání příspěvku. Pojďme udělat poslední nezbytnou věc k tomu, aby celé přidávání příspěvků fungovalo.
Jak jste si pravděpodobně všimli, připravili jsme si při kliknutí na tlačítko Share volání funkce app.add.post()
. Tato funkce dosud neexistuje, je čas ji vytvořit v našem JavaScriptovém souboru app.js v části app.add
:
// ...add: {post: function(){}},// ...
Funkce post()
se spustí při kliknutí na tlačítko Share. Nejdříve v ní zjistíme, jestli uživatel zadal jak příspěvek, tak svoji přezdívku. Následně vytvoříme POST dotaz na náš REST API server s parametry text a author, které uživatel zadal. Při úspěšném odeslání příspěvku vyčistíme políčka formuláře a přesměrujeme uživatele na seznam příspěvků, kde později uvidí svůj příspěvek.
// ...add: {post: function(){var text = $('#add-text');var author = $('#add-author');if (text.val() && author.val()){$.post('/posts', {text: text.val(), author: author.val()}, function(response){text.val('');author.val('');app.goTo('stories');});}elsealert('It is necessary to fill both text and your nickname.');}},// ...
Nezapomeňte změnit adresu /post
na celou adresu k vašemu REST API serveru.
Tímto máme přidávání příspěvků hotové. Zda-li vše funguje jak má si můžete jednoduše zkontrolovat náhledem do databáze.
Příště zajistíme, aby se na hlavní stránce naší webové aplikace zobrazovaly poslední přidané příspěvky.
Celý kód naší aplikace by nyní měl vypadat následovně:
.page {display: none;padding: 24px;margin-top: 54px;}
window.app = {stories: {start: function(){console.log('Stories');}},story: {},add: {post: function(){var text = $('#add-text');var author = $('#add-author');if (text.val() && author.val()){$.post('/posts', {text: text.val(), author: author.val()}, function(response){text.val('');author.val('');app.goTo('stories');});}elsealert('It is necessary to fill both text and your nickname.');}},init: function(){app.goTo('stories');},goTo: function(page){if (!app[page])page = 'stories';if (page == app.currentPage)return;if (app.currentPage){$('#page-'+app.currentPage).fadeOut(250, function(){$('#page-'+page).fadeIn(250);});}else$('#page-'+page).fadeIn(250);app.currentPage = page;if (app[page].start)app[page].start();$('nav li.active').removeClass('active');$('nav a[onclick="app.goTo(\'' + page + '\')"]').parent().addClass('active');}}$(document).ready(app.init);
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>The Wall</title><link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css"><link rel="stylesheet" href="screen.css"></head><body><nav class="navbar navbar-fixed-top navbar-dark" style="background-color: #0083ff;"><a class="navbar-brand" href="#">The Wall</a><ul class="nav navbar-nav float-xs-right"><li class="nav-item active"><a class="nav-link" href="#" onclick="app.goTo('stories')">Stories</a></li><li class="nav-item"><a class="nav-link" href="#" onclick="app.goTo('add')">Add</a></li></ul></nav><div id="page-stories" class="page">Latest stories</div><div id="page-story" class="page">Story detail</div><div id="page-add" class="page"><div class="col-xs-12" style="text-align: center;"><h1>What's on your mind?</h1></div><div class="col-xs-12 col-md-6"><label for="add-text">Your thoughts</label><textarea id="add-text" class="form-control" rows="5"></textarea></div><div class="col-xs-12 col-md-6"><label for="add-author">Your nickname</label><input id="add-author" type="text" class="form-control" maxlength="30"></div><div class="col-xs-12" style="text-align: center; margin: 24px;"><button class="btn btn-primary" onclick="app.add.post()">Share</button></div></div><script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script><script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script><script type="text/javascript" src="js/app.js"></script></body></html>