Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

6. diel - Jednoduchý redakčný systém v AngularJS - API článkov

V minulej lekcii, Jednoduchý redakčný systém v AngularJS - Štruktúra projektu , sme si pripravili projektovú štruktúru pre jednoduchý redakčný systém. Ako som sľúbil, dnes sa rovno bez zbytočných rečí vrhneme na implementáciu. Takže ideme na to! :)

Práca s API v AngularJS

Keďže aplikácie v AngularJS je vlastne single-page aplikácie v podobe hrubého klienta, stará sa v podstate o všetko v rámci svojej MVC architektúry. Snáď jedinú výnimku tvorí dáta. Všetky dáta sa štandardne spracovávajú na strane servera (kvôli zdieľanie dát medzi klientmi a validáciu bez zásahu klienta). Server na účely získania alebo vloženie / úpravy dát vystavuje nejaké API (A pplication P rogramming I nterface).

V AngularJS potom existuje hneď niekoľko možností ako s týmto API komunikovať. Pre našu aplikáciu som zvolil API v podobe tradičného REST (Re presentational S tate T ransfer) s komunikačným formátom JSON (J ava S Cripta O bject N otation). Ak tieto skratky snáď nepoznáte, odporúčam si o tejto problematike niečo viac naštudovať, napr. V miestnom podarenom článku Stopárov sprievodca REST API :)

Teraz teda k možnostiam komunikácie. Základné možností, snáď ako všade, je volať priamo jednotlivé HTTP metódy daného API. K tomu slúži AngularJS služba $http, ktorá je súčasťou priamo jeho jadra. Ak si ale povieme, že naše API bude viacmenej spĺňať štandard Restful, môžeme využiť službu $resource, ktorá je súčasťou modulu ngResource, ktorá nám všetko veľmi uľahčí. Vydáme sa teda touto cestou najmenšieho odporu :)

Model

Vytvoríme si službu pre obsluhu API článkov pomocou $resource.

App / services / articles.factory.js

Naša služba bude vyzerať nasledovne:

'use strict';

/** Model pro práci s články přes API. */
app.factory('Articles', function ($resource) {
    return $resource('/api/articles/:url', {url: '@url'});
});

A to je všetko, ideme domov :) Vážne, teraz vieme obsluhovať celej Restful API pre články, pomocou štandardizovaných metód v rámci $resource. Ale dobrá, poďme si to ešte trochu vysvetliť.

V prvom rade, prečo sme použili factory miesto service a aký je v tom rozdiel? Oboje definuje službu. Ovšem klasická service je už priamo funkciou danej služby, keď to factory vytvára a vracia nejaký nový objekt, ktorý onú službu definuje. Ovšem nenechajte sa zmiasť, oboje tvoria iba jednu inštanciu danej služby, ktorá bude k dispozícii cez DI.

V tomto prípade teda konštrukcie $resource nad danou URL adresou API vytvára nový objekt, ktorý keď vrátime z factory, bude definovaný ako služba v našej aplikácii. Chytré, že?

Inak URL adresa API tu má jeden dynamický parameter, čo bude URL adresa článku. Tento formát je práve predpísaný normou Restful API.

Dobre, máme teda vysvetlené, ale stále tu zostáva jeden problém ... To API predsa neexistuje! : -`

Tu začína tá ťažká časť, pretože ho musíte implementovať. Môžete si v podstate vybrať ktorýkoľvek programovací jazyk, nastaviť webový server a hurá do práce!

Ak sa vám ale nechce teraz odchádzať k serverovej implementáciu, existuje ešte jeden variant, podporovaná priamo v AngularJS. Môžeme si serverové API našej aplikácie nasimulovať v rámci lokálnej aplikácie! Čo to znamená? API budeme musieť stále implementovať, ale urobíme to ako súčasť našej klientskej aplikácie, kde následne využijeme modul ngMockE2E, ktorý nám umožní premapovať API otázky na volanie lokálne JS implementácie. Tejto metodiky sa v praxi využíva hlavne pre testovanie, ale občas aj pokiaľ napr. API nie je ešte hotové, ale vy s ním v aplikácii potrebujete pracovať. Tak ideme na to! :)

Simulácia API

Ako som opísal vyššie, musíme v podstate vytvoriť implementácii podobnú tej serverové, akurát v JS a následne ju premapovať v rámci AngularJS. Táto implementácia samotná teda nemá s AngularJS poťažmo nič spoločné, takže ak nemáte úplne radi čistý JS, pripomínam, že ju môžete pokojne urobiť priamo na serveri vo vašom obľúbenom jazyku.

Ja teda budem predpokladať, že JS viete dobre a nebudem sa zdržovať zbytočným vysvetľovaním. Prípadne si môžete odskočiť k miestnym kurzom JavaScriptu.

Adresárová štruktúra API

Najskôr si vytvoríme bokom adresárovú štruktúru pre naše API:

  • api/
    • entities/
      • article.js - Entita reprezentujúce článok.
    • model/
      • article-model.js - Model pre prácu s článkami.
    • api.js - Tu umiestnime mapovanie API v rámci našej AngularJS aplikácie.
Nezabudnite potom všetko pekne nalinkovať do index.html:
...
<!-- Simulace serverového API. -->
<script src="api/api.js"></script>
<script src="api/entities/article.js"></script>
<script src="api/models/article-model.js"></script>
...

Api / entities / article.js

Logicky som si vybral OOP implementačné prístup, tj. Pomocou entít reprezentujúcich dáta a ich ukladanie do pamäte. Začneme teda s entitou reprezentujúci článok:

'use strict';

/**
 * @typedef {Object} ArticleValues
 * @property {string} url - Unikátní URL adresa článku.
 * @property {string} [title] - Titulek článku.
 * @property {string} [content] - Text (obsah) článku.
 * @property {string} [description] - Krátký popis článku.
 */

/**
 * Reprezentuje datové záznamy článků v redakčním systému.
 * @constructor
 * @param {ArticleValues} values - Hodnoty záznamu článku.
 * @implements {ArticleValues}
 * @throws {Error} Jestliže hodnoty záznamu článku nejsou validní.
 */
function Article(values) {
    // Data článku.
    var data = {
        url: null,
        title: '',
        content: '',
        description: ''
    };

    // Definuj data článku jako jeho vlastnosti.
    Object.keys(data).forEach((function (key) {
        Object.defineProperty(this, key, {
            get: function () { return data[key]; },
            set: function (value) {
                if (typeof value !== 'string') throw new TypeError('Pole ' + key + ' musí být typu string!');
                if (!value) throw new Error('Pole ' + key + ' nemůže být prázdné!');
                data[key] = value;
            },
            enumerable: true
        });
    }).bind(this));

    // Inicializace povinné URL.
    this.url = values.url;

    // Automatická inicializace dalších nepovinných hodnot.
    for (var key in this)
        if (this.hasOwnProperty(key) && values.hasOwnProperty(key))
            this[key] = values[key];
}

Niekoho možno trochu prekvapí typová notácie JS dokumentácie. Používam tu JSDoc.

Api / models / article-model.js

Ďalej vytvoríme model pre správu článkov, hlavne ich ukladanie. Najskôr som si hovoril, že to urobím iba do pamäti, tzn pri obnovení stránky by sa všetko vynulovali. Potom som sa však rozhodol využiť perzistentné úložisko LocalStorage na strane klienta, keď už chceme písať tie moderné webové aplikácie. Tento model, pracujúce s lokálnym úložiskom, bude vyzerať nejako takto:

'use strict';

/**
 * Model poskytující metody pro správu článků v redakčním systému.
 * @constructor
 */
function ArticleModel() {
    /**
     * Vrátí seznam článků.
     * @returns {Article[]} - Seznam článků.
     */
    this.getArticles = function () {
        var articles = [];
        for (var key in localStorage) if (localStorage.hasOwnProperty(key))
            articles.push(this.getArticle(key));
        return articles;
    };

    /**
     * Vrátí článek podle jeho URL.
     * @param {string} url - URl článku.
     * @returns {null|Article} Článek s danou URL, nebo null, pokud takový neexistuje.
     */
    this.getArticle = function (url) { return JSON.parse(localStorage.getItem(url)); };

    /**
     * Uloží článek. Pokud již existuje článek s danou URL provede editaci.
     * @param {Article} article - Článek.
     */
    this.saveArticle = function (article) { localStorage.setItem(article.url, JSON.stringify(article)); };

    /**
     * Odstraní článek.
     * @param {string} url - URL článku.
     */
    this.removeArticle = function (url) { localStorage.removeItem(url); }
}

Api / api.js

Posledná časť API tvoria samotné mapovanie dotazov na vyššie implementované funkcie pomocou knižničného modulu ngMockE2E:

'use strict';

/** Konfigurace simulace serverového API. */
app.run(function ($httpBackend) {

    // Model pro práci s články.
    var articleModel = new ArticleModel();

    // Výchozí URL adresa simulovaného API.
    var apiUrl = '/api';

    // Adresa simulovaného API pro správu článků.
    var articlesApiUrl = apiUrl + '/articles';

    // Regex výraz pro dynamický parametr :url v adrese simulovaného API správy článků.
    var articleApiUrlRegex = new RegExp(articlesApiUrl.replace(/\//g, '\/') + '\/\\w+');

    // Předem připravené výchozí články.
    var defaultArticles = [
        new Article({
            url: 'uvod',
            title: 'Úvod',
            content: '<p>Vítejte na našem webu!</p>\r\n\r\n<p>Tento web je postaven na <strong>jednoduchém redakčním systému v AngularJS frameworku</strong>.</p>',
            description: 'Úvodní článek na webu v AngularJS'
        }),
        new Article({
            url: 'chyba',
            title: 'Stránka nebyla nalezena',
            content: '<p>Litujeme, ale požadovaná stránka nebyla nalezena. Zkontrolujte prosím URL adresu.</p>',
            description: 'Stránka nebyla nalezena.'
        })
    ];

    // Inicializuje výchozí články, pokud neexistují.
    for (var i = 0; i < defaultArticles.length; ++i)
        if (!articleModel.getArticle(defaultArticles[i].url))
            articleModel.saveArticle(defaultArticles[i]);

    // Mapování URL adres API na lokální data.

    // GET "/api/articles" vrací seznam všech aktuálních článků.
    $httpBackend.whenGET(articlesApiUrl).respond(function () {
        return [200, articleModel.getArticles()];
    });

    // GET "/api/articles/:url" vrací článek s danou URL.
    $httpBackend.whenGET(articleApiUrlRegex).respond(function (method, url) {
        var article = articleModel.getArticle(url.split('/')[3]);
        return (article ? [200, article] : [404, {}, {}, 'Článek nenalezen!']);
    });

    // POST "/api/articles/:url" uloží článek článek s danou URL.
    $httpBackend.whenPOST(articleApiUrlRegex).respond(function (method, url, data) {
        try {
            var article = new Article(angular.fromJson(data));
            articleModel.saveArticle(article);
            return [201, article, {Location: '/' + article.url}];
        } catch (error) {
            if (error instanceof Error) return [400, {}, {}, error.message];
            console.error(error);
            return [500];
        }
    });

    // DELETE "/api/articles/:url" smaže článek s danou URL.
    $httpBackend.whenDELETE(articleApiUrlRegex).respond(function (method, url) {
        articleModel.removeArticle(url.split('/')[3]);
        return [204];
    });

    // POST "/api/contact-messages" zpracuje poslanou kontaktní zprávu.
    $httpBackend.whenPOST(apiUrl + '/contact-messages').respond(function (method, url, data) {
        var message = angular.fromJson(data);
        if (parseInt(message.year) !== (new Date()).getFullYear()) return [400, {}, {}, 'Chybně vyplněný antispam!'];
        alert('Odesílatel: ' + message.email + '\n' + message.content);
        return [201, message];
    });

    // Požadavky na soubory šablon naší aplikace necháme projít.
    $httpBackend.whenGET(/templates\//).passThrough();
});

Myslím, že kód je dobre zdokumentovaný sám o sebe a ak rozumiete HTTP, nie je na ňom nič zložitého.

Len poznámka, implementoval som tu rovno aj API pre odosielanie správ kontaktného formulára, ktorý budeme implementovať neskôr v rámci nášho kurzu. To aby sme sa k API nemuseli zbytočne vracať a upravovať ho.

To je z dnešnej lekcie všetko. Myslím, že to aj bohato stačí. V lekcii budúci, Jednoduchý redakčný systém v AngularJS - Výpis článku , sa už budeme venovať zas len AngularJS a to konkrétne kontrolerům aj šablónam. Tým náš projekt sprevádzkujeme :)


 

Predchádzajúci článok
Jednoduchý redakčný systém v AngularJS - Štruktúra projektu
Všetky články v sekcii
AngularJS
Preskočiť článok
(neodporúčame)
Jednoduchý redakčný systém v AngularJS - Výpis článku
Článok pre vás napísal Jindřich Máca
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje převážně webovým technologiím, ale má velkou zálibu ve všem vědeckém, nejen ze světa IT. :-)
Aktivity