Blog za 5 minut

Jako příklad si uděláme jednoduchý blog. Ve skutečnosti byste sice asi použili nějakou rozšířenou blogovací platformu, ale je to často používaný a jasný příklad s jasnou potřebnou funkcionalitou.

Kompletní zdrojový kód příkladu najdete na https://github.com/JellyPot/tutorial/tree/01-01. Nebo máte-li nainstalovaný git, můžete ve své cvičné lokální složce JellyPot spustit příkaz
git init & git pull https://github.com/JellyPot/tutorial.git 01-01
(Jen si v takovém případě rozbalte jen samotný JellyPot bez boilerplate – to by vám pak git hlásil kolize.)

Začneme tvorbou datového modelu, tedy deklarací proměnných a jejich typů, do kterých se bude ukládat obsah webu. To se dělá v souboru Site.config. Ten je ve formátu XML a jeho obsah JellyPot používá k automatickému generování databáze i administračního rozhraní. Ani o jedno se nemusíte nijak starat, stačí něco napsat do Site.config a zbytek udělá JellyPot okamžitě sám.

Site.config ze standardního boilerplate upravte jméno a název projektu:

<?xml version="1.0" encoding="utf-8" ?>
<JellyPotDef xmlns="urn:Bet:ns:JellyPot:SiteConfig">
    <Project name="MujBlog01" title="Můj cvičný blog" />
    <Vars>
        ...
    </Vars>
    <Types>
        ...
    </Types>
</JellyPotDef>

Potřebné typy proměnných a prvků kolekcí se deklarují v sekci <Types>. Typ si můžete představit jako množinu položek elementárních datových typů, která představuje strukturu nějakého objektu (nebo např. strukturu entity v ERD/ERM). Pokud to teď zní záhadně, vydržte, za chvíli to začne dávat smysl.

Typy použijeme při deklaraci proměnných a kolekcí v sekci <Vars>. Právě ty uchovávají data a představují konkrétní objekty nebo množiny objektů. Je možné je editovat v administraci a jejich obsah budete zobrazovat ve webových stránkách.

Pořadí sekcí <Vars><Types> je volné, ale doporučujeme – možná trochu nelogicky – jako první umísťovat <Vars>. Jejich obsah bývá podstatně kratší a Site.config je tak přehlednější.

Pro blog budeme potřebovat typ reprezentující článek. Chceme, aby obsahoval tyto údaje:

  • nadpis článku – krátký text
  • datum vydání článku – datum
  • samotný text článku – delší text

Typ pojmenujeme BlogPostItem a do sekce <Types> ho zapíšeme takto:

...
<Types>
    <Type name="BlogPostItem">
        <SimpleText name="title" />
        <DateTime name="published" />
        <Text name="text" />
    </Type>
</Types>
...

Tagy SimpleText, DateTime, atd. určují typ datových polí. Atribut name pak je jméno každého pole.

Protože předpokládáme, že článků bude na blogu víc, že nebude jen jeden, potřebujeme rovnou celou kolekci prvků typu BlogPostItem. Do sekce <Vars> proto zapíšeme:

...
<Vars>
    <Collection name="blogPost" itemType="BlogPostItem" />
</Vars>
...

Výsledkem je kolekce prvků, kde každý z nich obsahuje nadpis (title), datum (published) a text (text). Kompletní seznam všech datových typů, s kterými můžete v JellyPot pracovat najdete v referenční části.

Atribut name určuje jméno kolekce, pod kterým k ní budeme moct přistupovat v šablonách .aspx. Do itemType jsme uvedli jméno typu prvků kolekce – tedy jméno, které jsme zapsali do atributu name v deklaraci typu.

Kromě kolekcí existují i obyčejné proměnné, ty si ukážeme v další části, kdy blog doplníme stránkou „O autorovi“.

Pojmenovávat typy, proměnné a kolekce můžete celkem libovolně, jen mějte na paměti, že se jedná o identifikátor, který musí začínat písmenem a může obsahovat i podtržítko nebo číslo. Jména jsou case-sensitive. Doporučujeme držet se konvence.

Na adrese http://localhost/mujblog/admin bychom nyní měli mít administraci webu, v které můžeme přidávat a editovat články. Pár si jich tam dejte, ať máme v dalším kroku co zobrazit na webu.

.aspx – šablony HTML stránek

Kód, který generuje výsledné HTML stránky se umísťuje do souborů .aspx. Každý z nich představuje šablonu pro jednu stránku (resp. jednu skupinu stránek) . Podobně jako datové struktury v Site.config se i tento kód píše v jazyce založeném na XML.

K datovým polím proměnných a prvků kolekcí ve stránce se přistupuje pomocí tzv. controlů. Ty buď přímo vypisují obsah daného pole nebo s ním nějak pracují. Control je termín používaný v ASP.NET. Je to prostě prvek na stránce, který vypadá jako namachrovaný HTML tag. Zpracovává se na straně serveru. Control si lze také představit jako jakýsi placeholder, který JellyPot promění v požadovaný obsah, když generuje výstupní HTML. Controly můžou např.:

  • vypsat obsah daného pole v textové podobě – to se nám bude hodit pro výpis nadpisu, data a textu. To je <je:item>.
  • vypsat HTML tag obrázku – s tím se setkáme hned v příští kapitole při tvorbě stránky „O autorovi“. To je <je:img>
  • iterovat přes prvky kolekce. K tomu slouží <je:repeater> a umožňuje tak postupně vypsat obsah polí všech jejích prvků. My ho použijeme k vypsání přehledu všech článků na homepage.
  • vytváře dynamické odkazy na stránky. To se nám bude hodit také na homepage pro vytvoření odkazů na jednotlivé články. Používá se k tomu <je:aVar>.

Jednoduchý příklad by mohl vypadat třeba takto:

<je:item runat="server" field="about.name" />

Tím bychom vypsali obsah pole name proměnné about, jak je patrné z obsahu atributu field. Atribut runat="server" je povinný, říká prostředí ASP.NET, že má control zpracovat. Přesně s tímhle controlem se setkáme v příští kapitole ve stránce „O autorovi“.

A teď už k jednotlivým stránkám...

Homepage

Kód pro homepage patří do souboru /cs/default.aspx. Na ní vypíšem přehled všech článků, vždy s odkazem na celý článek. (Zvýrazněné části jsou controly JellyPot, zbytek obyčejné HTML.)

<%@ Page %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <je:stylelink runat="server" href="/assets/css/main.css" />
        <title>Můj cvičný blog</title>
    </head>
    <body>
        <header>
            <je:a runat="server" class="logo" href="/cs/">Můj blog</je:a>
            <nav>
                <je:a runat="server" href="/cs/">Homepage</je:a>
            </nav>
        </header>
        <main>
            <je:repeater runat="server" source="blogPost">
                <item>
                    <h2>
                        <je:avar runat="server" href="/cs/blogpost.aspx">
                            <je:item runat="server" field=".title" />
                        </je:avar>
                    </h2>
                    <je:item runat="server"
                             field=".published"
                             tag="time" />
                    <je:item runat="server"
                             field=".text"
                             format="length: 300"
                             tag="p" />
                </item>
            </je:repeater>
        </main>
    </body>
</html>

Controlem <je:repeater> procházíme kolekci všech článků blogPost. Ten obsahuje template item a ten pak obsahuje kód pro každý vypisovaný prvek. (Kromě template item existují např. i header nebo footer, více viz reference <je:repeater>. Pomocí jednotlivých controlů <je:item> vypisujeme obsah polí title, datetext. Jejich jména v atributu field se uvádí s tečkou na počátku – ta nám zařídí, že se hodnota vezme z aktuálního kontextu určeného <je:repeater>.

Control <je:stylelink> vypíše odkaz na soubor s CSS stylem. Control <je:a> vytvoří statický odkaz na zadané URL. Zajímavější je control <je:aVar>. Ten generuje dynamické odkazy na stránku blogpost.aspx, do URL ale navíc připojí identifikátor právě procházeného prvku.

Když se podíváte na http://localhost/mujblog/, měli byste vidět seznam článků, které jste naplnili do administrace. Prostudujte i vygenerovaný kód. Pomůže vám to pochopit, co se v šabloně vlastně děje.

Blogpost

Ještě uděláme stránku článku samotného. Vytvořte soubor /cs/blogpost.aspx s následujícím obsahem (controly JellyPot opět zvýrazněné):

<%@ Page %>
<je:container runat="server" expect="blogPost">
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <je:stylelink runat="server" href="/assets/css/main.css" />
        <title><je:item runat="server" field=".title" /></title>
    </head>
    <body>
        <header>
            <je:a runat="server" class="logo" href="/">Můj blog</je:a>
            <nav>
                <je:a runat="server" href="/">Homepage</je:a>
            </nav>
        </header>
        <main>
            <je:item runat="server" field=".title" tag="h1" />
            <je:item runat="server" field=".published" tag="time" />
            <je:item runat="server" field=".text" tag="p" />
        </main>
    </body>
</html>
</je:container>

Všimněte si, že výpis článku je téměř identický s jeho výpisem na homepage. Největší rozdíl je, že tu chybí <je:repeater> – nepotřebujeme procházet články, zobrazujeme jen jeden – ale objevil se nám tu <je:container> a dokonce jsme do něj zabalili celou stránku. Ten podle identifikátoru prvku kolekce předaného v URL zařídí, že všechny relativní cesty k polím proměnných (tj. kde hodnota atributu field začíná tečkou), jsou v kontextu předané proměnné. Prostě se vypíše článek, na který jsme klikli, a ne nějaký jiný z kolekce. Atribut expect pak určuje, prvek které kolekce se očekává. Pokud bychom předali identifikátor prvku z jiné kolekce nebo pokud bychom nepředali žádný, zobrazila by se chybová stránka 500 (resp. 404 v ostrém provozu). My použili expect="blogPost" – všimněte si, že to odpovídá atributu source controlu <je:repeater> na homepage. Takto je to téměř vždy.

Připomínky a postřehy

Máte-li nějakou připomínku, dobrý nápad, něco není úplně pochopitelné nebo jste našli v tutoriálu chybu, rádi od vás uslyšíme.