IMA.js – setup aplikace a vytvoření modelů

V tomto díle začneme psát naši ukázkovou aplikaci. Povedeme Vás od úplného začátku až po finální deployment do produkce. V průběhu si ukážeme využití všech hlavních vlastností IMA.js a jak vytvořit často používané prvky webových aplikací.

Minule jsme si framework IMA.js představili, dnes se pustíme do práce.

Ukázková aplikace bude zobrazovat počasí v místě, které:

  • uživatel vyhledá pomocí našeho vyhledávání
  • bude zadáno v URL parametru (SEO optimalizace)
  • uživatel nastaví a uloží jej do cookies.
  • je nastaveno v aplikaci jako výchozí, pokud se nepoužije žádná z výše uvedených možností.

Pro jednoduchost pojmenujeme aplikaci WeatherApp.

Devtooly

Než se pustíme do samotného vývoje ukázkové aplikace, rádi bychom zmínili naše vývojové nástroje v podobě rozšíření do prohlížeče Google Chrome, které si můžete stáhnout zde. Tyto nástroje poskytují jednoduchý způsob, jak sledovat a ladit jednotlivá volání a události, které ve vaší aplikaci probíhají.

Vývojovým nástrojům se budeme blíže věnovat v následujících dílech, pokud však chcete, můžete je začít používat už teď. Případně si o jejich možnostech něco pročíst v dokumentaci.

Setup

Slíbili jsme, že to vezmeme od začátku, ale také to nebudeme prodlužovat. Na instalaci taky není nic zajímavého.

K vývoji budete potřebovat:

  • Node.js ve verzi 10 a výše
  • NPM ve verzi 6 a výše.
cd <-- Váš adresář kde máte projekty -->
npx create-ima-app weather-app

Při dotazu na volbu typu projektu vyberte Empty. IMA.js totiž nabízí 3 demo „aplikace“, ze kterých si můžete vybrat. Jejich ukázky najdete na imajs.io/examples.

Pro naši aplikaci postačí základní Empty (Hello world!). Můžete však použít i Todos (TODO list aplikace) nebo Feed (Twitter-like feed aplikace).

Tímto máme vytvořený skeleton aplikace a nainstalované závislosti.

Adresářová struktura

V adresáři naší aplikace vzniklo několik podadresářů: appbuildnode_modules a server. My se budeme zabývat pouze adresářem app (ostatní jsou vysvětleny svým názvem).

  • assets – obsahuje soubory, které jsou zpracovány pre-processory a zkopírovány do build adresáře.
    • less – LESS soubory definující obecná pravidla, makra, mixins a základní strukturu UI.
    • static – jakékoliv soubory, které nepotřebují preprocessing (JS soubory 3. stran, obrázky, …)
  • component – naše React komponenty, které budeme používat ve views. Více o komponentách si řekneme v 3. díle tohoto seriálu.
  • config – konfigurační soubory aplikace. Nyní se konfigurací nebudeme zabývat a ukážeme si jednotlivé možnosti až je budeme potřebovat v průběhu vytváření aplikace.
  • page – controllery, views a LESS soubory k views.
    • error – stránka, která se zobrazí pokud se vyskytne chyba v průběhu aplikace.
    • home – hlavní stránka aplikace
    • notFound – stránka, která se zobrazí pokud uživatel zadá URL na neexistující routu.

Adresáře assets a config jsou povinné a měly by se v aplikaci vždy nacházet. Ostatní jsou volitelné a můžete si je přejmenovat. Je však potřeba upravit některé konfigurace a přihlížet k tomu v následném vývoji aplikace.

Modely

Modely nám v IMA.js aplikacích umožňují pracovat s daty. Pomocí modelů se data načítají, odesílají a transformují. V našem případě budeme využívat základní sestavu modelů (ServiceEntityResource a Factory).

  • Entity představuje objekt držící data nějakého celku (uživatele, článku, …).
  • Factory vytváří ze surových dat instance Entity.
  • Resource stahuje data ze serveru pomocí HTTP požadavků.
  • Service se stará o načítání dat z Resource a následné vytvoření entit pomocí Factory.

Může se vám zdát, že Factory a Resource jsou zbytečné. Data můžeme přece stahovat už v Service a tam také vytváře instance Entity. Ano to jistě můžeme, ale jen v jednodušších případech. Jak se bude aplikace rozrůstat a začnou se objevovat zanořené závislosti, věřte, že samostatné Factory a Resource přijdou vhod.

Pokud by aplikace využívala REST API, můžeme Factory a Resource úplně nahradit použitím @ima/plugin-rest-client. Více o tomto pluginu se dočtete v jeho README.

Na začátku jsme si stanovili, co naše aplikace bude umět. Podle toho si teď rozvrhneme datovou strukturu a vytvoříme modely.

1. Proxy

Data o počasí nám poskytne služba Počasí.cz přes svoje API rozhraní https://wapi.pocasi.seznam.cz. My ale na toto API rozhraní budeme přistupovat přes naši proxy. Vyhneme se tak problémům s CORS.

Proxy vytvoříme na stejném serveru, který se stará o výdej webové aplikace. Ve výchozím stavu je server připravený pouze pro jednu proxy. My jich však budeme potřebovat více. V souboru weather-app/server/server.js – funkce runNodeApp – najdeme řádek:

.use(environment.$Proxy.path + '/', proxy(environment.$Proxy.server, environment.$Proxy.options)

Vidíme, že server používá Express framework a pro proxy jeden z jeho doplňků express-http-proxy. Nastavení proxy se nachází v souboru weather-app/app/environment.js. Soubor obsahuje jednoduchý Node.js modul, který vrací objekt. Hlavními klíči objektu jsou prodtest a dev. Tyto klíče představují nastavení pro jednotlivé vývojové prostředí s tím, že všechny vycházejí z prod prostředí.

Poznámka: Tento styl konfigurace (prod – test – dev) si zapamatujte, neobjevuje se naposledy.

Naši proxy potřebujeme nastavit pouze v produkčním nastavení.

// app/environment.js

module.exports = (() => {
    return {
        prod: {
            // ...
            $Proxy: {
                path: '/api',   // na této URL bude proxy poslouchat
                server: 'https://wapi.pocasi.seznam.cz', // zde bude proxy přeposílat požadavky
                options: {
                    proxyReqPathResolver: function (req) {
                        const queryString = req.url.split('?')[1];

                        return '/v2/forecast' + (queryString ? '?' + queryString : '');
                    }
                }
            }
        }
        // ...
    }

2. Načítání dat o předpovědi počasí

V předchozím bodě jsme si nastavili proxy, která poslouchá na URL /api. Aby jsme URL neopakovali v aplikaci několikrát, přidáme ji do nastavení v souboru weather-app/app/config/settings.js. Zde se používá stejný styl konfigurace jako v environment.js.

// app/config/settings.js

export default (ns, oc, config) => {
  // ...
  return {
    prod: {
        // ...
        App: {
            api: config.$Protocol + '//' + config.$Host + '/api'
        }
    }
  }
}

V adresáři weather-app/app/model vytvoříme podadresář pro předpověď počasí – forecast. V tomto adresáři vytvoříme soubory  ForecastService.jsForecastResource.jsForecastFactory.js a ForecastEntity.js.

Začneme vytvořením ForecastResource. V aplikaci IMA.js funguje DI (Dependency Injection) skrz OC (Object Container). Více o OC a DI se dočtete v dokumentaci.

// app/model/forecast/ForecastResource.js
import { HttpAgent } from '@ima/core';

export default class ForecastResource {
	static get $dependencies() {
		return [HttpAgent, '$Settings.App.api']; // $Settings umožňuje vkládat nastavení pomocí DI
	}

	constructor(http, apiUrl) {
		this._http = http;
		this._api = apiUrl;
    }

    async getForecast(lat, lon) {
		const response = await this._http.get(
            this._api,
            { lat, lon, include: ['place', 'daily'] } // query parametry
        );

		return response.body;
	}
}

Následuje ForecastFactory. Prozatím bude obsahovat jen jednu metodu, která z předaných dat vytvoří instanci ForecastEntity.

// app/model/forecast/ForecastFactory.js
import ForecastEntity from './ForecastEntity';

export default class ForecastFactory {

    static get $dependencies() {
        return [];
    }

    createEntity(data) {
        return new ForecastEntity(data);
    }
}

ForecastEntity pouze vezme data z constructoru a vytvoří si z nich vlastní properties.

// app/model/forecast/ForecastEntity.js
export default class ForecastEntity {

    constructor(data) {
        this.place = data.place;

        this.daily = data.daily;

        Object.freeze(this);
    }
}

ForecastService bude obsahovat prozatím pouze jednu metodu getForecast. Ta pomocí ForecastResource načte data ze serveru a poté vytvoří instance ForecastEntity pomocí ForecastFactory.

// app/model/forecast/ForecastService.js
import ForecastResource from './ForecastResource';
import ForecastFactory from './ForecastFactory';

export default class ForecastService {
    static get $dependencies() {
        return [ForecastResource, ForecastFactory];
    }

    constructor(resource, factory) {
        this._resource = resource;
        this._factory = factory;
    }

    async getForecast(lat, lon) {
        const result = await this._resource.getForecast(lat, lon);

        return this._factory.createEntity(result);
    }

Vytvořili jsme také model pro geocoder – vyhledávání místa podle názvu z URL. Nebudeme zde ale rozepisovat kompletní postup, protože se v mnohém shoduje s modelem pro předpověď počasí. Kompletní podobu geocoder modelu najdete v kódu ukázkové aplikace.

Závěr

Po tomto díle byste měli mít nainstalovanou a funkční IMA.js aplikaci. Zároveň jsme si připravili datovou vrstvu (modely) pro další díl, ve kterém si ukážeme, jak data načítat a zobrazovat.

Vojta působí v Seznamu od roku 2016, aktuálně na pozici senior programátor UI. Nejprve pracoval pro oddělení Map na projektu Jízdní řády a později přešel do oddělení obsahových služeb, kde se jako i ostatní programátoři začal podílet na vývoji IMA.js frameworku.

Komentáře: 2

Přehled komentářů

Antonin Bouchal Ima vs Next
shemale01 Re: Ima vs Next
Zdroj: https://www.zdrojak.cz/?p=23600