Ember.js:Perfektní rámec pro webové aplikace

Ember.js je názorný frontendový JavaScript framework, o který je v poslední době velký zájem. Tento článek představí některé klíčové koncepty frameworku a zároveň s ním vytvoří jednoduchou aplikaci, aby ukázal základní příklad toho, co je schopen produkovat.

Naší ukázkovou aplikací bude Váleček kostkami, včetně možnosti hodit kostkami a zobrazit historii všech dosud provedených hodů kostkou. Plně funkční verze této aplikace je dostupná na Github

Rámec Ember.js spojuje mnoho moderních konceptů a technologií JavaScriptu do jednoho balíčku, mimo jiné:

  • Použití nástroje Babel transpiler pro podporu ES2016 po celou dobu.
  • Testovací podpora na úrovni Unit, Integration a Acceptance jako standard, založená na Testem a QTest.
  • Vytváření aktiv pomocí Broccoli.js.
  • Podpora pro živé načítání pro kratší dobu vývojového cyklu.
  • Vytváření šablon pomocí syntaxe značek Handlebars.
  • První vývoj směrování adres URL, aby bylo zajištěno, že přímé odkazy jsou v celém rozsahu plně podporovány.
  • Úplná datová vrstva postavená na rozhraní JSON API, ale připojitelná pro jakýkoli přístup k rozhraní API, který potřebujete.

Abyste mohli pracovat s Ember.js, předpokládá se, že máte aktuální instalaci Node.js a npm. Pokud ne, lze je stáhnout a nainstalovat z webu Node.js.

Je třeba také zmínit, že Ember je čistě frontendový framework. Má řadu způsobů interakce s backendem dle vašeho výběru, ale tento backend není žádným způsobem zpracováván samotným Emberem.

Představujeme ember-cli

Velká část výkonu Ember.js pochází z jeho rozhraní příkazového řádku (CLI). Tento nástroj – známý jako ember-cli – pohání velkou část životního cyklu vývoje aplikace Ember.js, počínaje vytvořením aplikace, přes přidání funkcí do ní až po spuštění testovacích sad a spuštění skutečného projektu ve vývojovém režimu.

Téměř vše, co děláte při vývoji aplikace Ember.js, bude na určité úrovni zahrnovat tento nástroj, takže je důležité pochopit, jak jej nejlépe používat. Budeme to využívat v celém tomto článku.

První věc, kterou musíme udělat, je zajistit, aby byl Ember.js CLI správně nainstalován a aktuální. To se provede instalací z npm následovně:

$ npm install -g ember-cli

a můžeme zkontrolovat, zda byla úspěšně nainstalována spuštěním následujícího příkazu:

$ ember --version
ember-cli: 2.15.0-beta.1
node: 8.2.1
os: darwin x64

Vytvoření své první aplikace Ember.js

Jakmile je ember-cli nainstalován, jste připraveni začít vytvářet aplikaci. Toto je první místo, kde budeme používat nástroj Ember.js CLI – vytváří celou strukturu aplikace a nastavuje vše tak, aby bylo připraveno ke spuštění.

$ ember new dice-roller
installing app
  create .editorconfig
  create .ember-cli
  create .eslintrc.js
  create .travis.yml
  create .watchmanconfig
  create README.md
  create app/app.js
  create app/components/.gitkeep
  create app/controllers/.gitkeep
  create app/helpers/.gitkeep
  create app/index.html
  create app/models/.gitkeep
  create app/resolver.js
  create app/router.js
  create app/routes/.gitkeep
  create app/styles/app.css
  create app/templates/application.hbs
  create app/templates/components/.gitkeep
  create config/environment.js
  create config/targets.js
  create ember-cli-build.js
  create .gitignore
  create package.json
  create public/crossdomain.xml
  create public/robots.txt
  create testem.js
  create tests/.eslintrc.js
  create tests/helpers/destroy-app.js
  create tests/helpers/module-for-acceptance.js
  create tests/helpers/resolver.js
  create tests/helpers/start-app.js
  create tests/index.html
  create tests/integration/.gitkeep
  create tests/test-helper.js
  create tests/unit/.gitkeep
  create vendor/.gitkeep
NPM: Installed dependencies
Successfully initialized git.

$

To způsobilo vytvoření celé aplikace, která je připravena ke spuštění. Dokonce nastavil Git jako ovládání zdroje pro sledování vaší práce.

Poznámka: Pokud chcete, můžete zakázat integraci Git a můžete dát přednost Yarn před npm. Toto a mnohem více popisuje nápověda k nástroji.

Nyní se podívejme, jak to vypadá. Spuštění aplikace Ember pro účely vývoje se – opět – také provádí pomocí ember-cli:

$ cd dice-roller
$ ember serve
Livereload server on http://localhost:49153
'instrument' is imported from external module 'ember-data/-debug' but never used
Warning: ignoring input sourcemap for vendor/ember/ember.debug.js because ENOENT: no such file or directory, open '/Users/coxg/source/me/writing/repos/dice-roller/tmp/source_map_concat-input_base_path-2fXNPqjl.tmp/vendor/ember/ember.debug.map'
Warning: ignoring input sourcemap for vendor/ember/ember-testing.js because ENOENT: no such file or directory, open '/Users/coxg/source/me/writing/repos/dice-roller/tmp/source_map_concat-input_base_path-Xwpjztar.tmp/vendor/ember/ember-testing.map'

Build successful (5835ms) – Serving on http://localhost:4200/



Slowest Nodes (totalTime => 5% )              | Total (avg)
----------------------------------------------+---------------------
Babel (16)                                    | 4625ms (289 ms)
Rollup (1)                                    | 445ms

Nyní jsme připraveni vyrazit. Aplikace běží na http://localhost:4200 a vypadá takto:

Je také spuštěna služba LiveReload, která automaticky sleduje změny v souborovém systému. To znamená, že můžete mít neuvěřitelně rychlou dobu obratu při ladění designu vašeho webu.

Zkusíme to?

Úvodní stránka nám již říká, co máme dělat, takže pojďme změnit hlavní stránku a uvidíme, co se stane. Změníme app/templates/application.hbs soubor vypadat takto.

This is my new application.

{{outlet}}

Poznámka: {{outlet}} tag je součástí toho, jak Routing funguje v Emberu. Tomu se budeme věnovat později.

První věc, které si všimnete, je výstup z ember-cli, který by měl vypadat následovně:

file changed templates/application.hbs

Build successful (67ms) – Serving on http://localhost:4200/

Slowest Nodes (totalTime => 5% )              | Total (avg)
----------------------------------------------+---------------------
SourceMapConcat: Concat: App (1)              | 9ms
SourceMapConcat: Concat: Vendor /asset... (1) | 8ms
SimpleConcatConcat: Concat: Vendor Sty... (1) | 4ms
Funnel (7)                                    | 4ms (0 ms)

To nám říká, že si všiml, že jsme změnili šablonu a vše přestavěli a restartovali. Do této části jsme se nijak nezapojili.

Nyní se podíváme na prohlížeč. Pokud máte nainstalovaný a spuštěný LiveReload, nebudete muset ani obnovovat prohlížeč, aby se to načetlo, jinak budete muset znovu načíst aktuální stránku.

Není to příliš vzrušující, ale téměř bez úsilí z naší strany jsme toho dosáhli.

Navíc získáme plně nastavenou testovací sadu připravenou ke spuštění. To se – nepřekvapivě – spouští také pomocí nástroje Ember, a to následovně:

$ ember test
⠸ Building'instrument' is imported from external module 'ember-data/-debug' but never used
⠴ BuildingWarning: ignoring input sourcemap for vendor/ember/ember.debug.js because ENOENT: no such file or directory, open '/Users/coxg/source/me/writing/repos/dice-roller/tmp/source_map_concat-input_base_path-S8aQFGaz.tmp/vendor/ember/ember.debug.map'
⠇ BuildingWarning: ignoring input sourcemap for vendor/ember/ember-testing.js because ENOENT: no such file or directory, open '/Users/coxg/source/me/writing/repos/dice-roller/tmp/source_map_concat-input_base_path-wO8OLEE2.tmp/vendor/ember/ember-testing.map'
cleaning up...
Built project successfully. Stored in "/Users/coxg/source/me/writing/repos/dice-roller/tmp/class-tests_dist-PUnMT5zL.tmp".
ok 1 PhantomJS 2.1 - ESLint | app: app.js
ok 2 PhantomJS 2.1 - ESLint | app: resolver.js
ok 3 PhantomJS 2.1 - ESLint | app: router.js
ok 4 PhantomJS 2.1 - ESLint | tests: helpers/destroy-app.js
ok 5 PhantomJS 2.1 - ESLint | tests: helpers/module-for-acceptance.js
ok 6 PhantomJS 2.1 - ESLint | tests: helpers/resolver.js
ok 7 PhantomJS 2.1 - ESLint | tests: helpers/start-app.js
ok 8 PhantomJS 2.1 - ESLint | tests: test-helper.js

1..8
# tests 8
# pass  8
# skip  0
# fail  0

# ok

Všimněte si, že výstup hovoří o PhantomJS. Je to proto, že existuje plná podpora integračních testů, které se spouštějí v prohlížeči a ve výchozím nastavení se v prohlížeči PhantomJS spouštějí bezhlavě. K dispozici je plná podpora pro jejich spouštění v jiných prohlížečích, pokud si přejete, a při nastavování kontinuální integrace (CI) stojí za to to udělat, aby vaše aplikace fungovala správně ve všech podporovaných prohlížečích.

Jak je strukturována aplikace Ember.js

Než se dostaneme k samotnému psaní naší aplikace, pojďme prozkoumat, jak je strukturována na souborovém systému. ember new příkaz výše vytvoří na vašem počítači celou adresářovou strukturu se spoustou různých částí. Pochopení toho všeho je důležité pro efektivní práci s nástrojem a vytváření úžasných projektů.

Na úplně nejvyšší úrovni si všimnete následujících souborů a adresářů:

  • README.md – Toto je standardní soubor readme popisující aplikaci
  • package.json – Toto je standardní konfigurační soubor npm popisující vaši aplikaci. To se používá především pro správnou instalaci závislostí.
  • ember-cli-build.js – Toto je konfigurace pro nástroj Ember CLI, který pohání naše sestavení
  • testem.js – Toto je konfigurace pro testovací rámec. To vám umožňuje mimo jiné definovat prohlížeče, které by měly být použity ke spouštění testů v různých prostředích.
  • aplikace/ – Toto je skutečná aplikační logika. Hodně se zde děje, co bude popsáno níže.
  • config/ – Toto je konfigurace pro aplikaci
    • config/targets.js – Toto je seznam podporovaných prohlížečů. Toto používá Babel k zajištění toho, aby byl Javascript transpilován takovým způsobem, aby všechny fungovaly.
    • config/environment.js – Toto je hlavní konfigurace pro vaši aplikaci. Zde by mělo být uvedeno vše, co je pro aplikaci potřeba, ale může se to lišit od jednoho prostředí k druhému.
  • veřejné/ – Toto jsou jakékoli statické prostředky, které chcete zahrnout do své aplikace. Například obrázky a písma.
  • prodejce/ – Sem jdou všechny závislosti frontendu, které nejsou spravovány systémem sestavení
  • testy/ – Zde směřují všechny testy
    • testy/jednotka – Toto jsou všechny testy jednotek pro aplikaci
    • testy/integrace – Toto jsou všechny integrační testy pro aplikaci

Celková struktura stránky (včetně obsahu třetích stran)

Než se dostaneme příliš dopředu, dejme naší stránce určitou formu struktury. V tomto případě přidáme framework Materialise CSS, abychom mu dodali lepší vzhled a dojem.

Přidání podpory pro obsah třetích stran, jako je tento, lze provést několika způsoby:

  • Přímé propojení s obsahem v externí službě, jako je CDN
  • Pomocí správce balíčků, jako je npm nebo Bower, jej nainstalujete za nás
  • Zahrnutí přímo do naší aplikace.
  • Použití doplňku Ember, pokud je k dispozici

Bohužel doplněk pro Materialize zatím nefunguje s nejnovější verzí Ember.js, takže místo toho jednoduše odkážeme na zdroje CDN z naší hlavní stránky. Abychom toho dosáhli, budeme aktualizovat app/index.html , což je hlavní struktura stránky, do které se naše aplikace vykresluje. Jednoduše přidáme odkazy CDN pro jQuery, Google Icon Font a Materialise.

<!-- Inside the Head section -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.1/css/materialize.min.css">

<!-- Inside the Body section -->
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.1/js/materialize.min.js"></script>

Nyní můžeme aktualizovat hlavní stránku, aby zobrazovala naši základní šablonu. To se provádí úpravou app/templates/application.hbs vypadat takto:

<nav>
    <div class="nav-wrapper">
        <a href="#" class="brand-logo">
            <i class="material-icons">filter_6</i>
            Dice Roller
        </a>
        <ul id="nav-mobile" class="right hide-on-med-and-down">
        </ul>
    </div>
</nav>

<div class="container">
    {{outlet}}
</div>

Tím se nám v horní části obrazovky zobrazí Materialize Nav s kontejnerem obsahujícím toto {{outlet}} dříve zmíněná značka.

Při návštěvě ve vašem prohlížeči to pak vypadá takto:

Takže co je to outlet štítek? Ember funguje na základě tras, kde každá trasa je považována za potomka nějaké jiné trasy. Nejvýše umístěnou trasu zpracovává Ember automaticky a vykresluje šablonu app/templates/application.hbs .

outlet tag určuje, kde Ember vykreslí další cestu v aktuální hierarchii – takže trasa první úrovně je vykreslena do této značky v application.hbs , trasa druhé úrovně se vykreslí do této značky v šabloně první úrovně atd.

Vytvoření nové trasy

V aplikaci Ember.js je každá stránka, kterou lze navštívit, přístupná prostřednictvím trasy. Mezi adresou URL, kterou prohlížeč otevře, a trasou, kterou aplikace vykresluje, existuje přímé mapování.

Nejjednodušší způsob, jak to vidět, je na příkladu. Pojďme do naší aplikace přidat novou cestu, která uživateli umožní skutečně házet kostkami. Opět se to provádí pomocí nástroje ember-cli.

$ ember generate route roll
installing route
  create app/routes/roll.js
  create app/templates/roll.hbs
updating router
  add route roll
installing route-test
  create tests/unit/routes/roll-test.js

Tento jeden příkaz nám dal:

  • Obslužný program pro trasu – app/routes/roll.js
  • Šablona pro trasu – app/templates/roll.hbs
  • Test trasy – tests/unit/routes/roll-test.js
  • Aktualizovali jsme konfiguraci routeru, aby věděli o této nové trase – app/router.js

Podívejme se na to v akci. Prozatím budeme mít velmi jednoduchou stránku, která nám umožní házet kostkami. Chcete-li tak učinit, aktualizujte app/templates/roll.hbs takto:

<div class="row">
    <form class="col s12">
        <div class="row">
            <div class="input-field col s12">
                <input placeholder="Name" id="roll_name" type="text" class="validate">
                <label for="roll_name">Name of Roll</label>
            </div>
        </div>
        <div class="row">
            <div class="input-field col s6">
                <input placeholder="Number of dice" id="number_of_dice" type="number" class="validate" value="1">
                <label for="number_of_dice">Number of Dice</label>
            </div>
            <div class="input-field col s6">
                <input placeholder="Number of sides" id="number_of_sides" type="number" class="validate" value="6">
                <label for="number_of_sides">Number of Sides</label>
            </div>
        </div>
        <div class="row">
            <button class="btn waves-effect waves-light" type="submit" name="action">
                Roll Dice
                <i class="material-icons right">send</i>
            </button>
        </div>
    </form>
</div>

{{outlet}}

Poté navštivte http://localhost:4200/roll a podívejte se na výsledek:

Teď musíme být schopni se sem dostat. Ember to velmi zjednodušuje pomocí link-to štítek. To vezme (mimo jiné) název trasy, na kterou uživatele posíláme, a poté vykreslí značku, aby se tam uživatel dostal.

V našem případě budeme aktualizovat app/templates/application.hbs obsahovat následující:

<ul id="nav-mobile" class="right hide-on-med-and-down">
    {{#link-to 'roll' tagName="li"}}
        <a href="roll">Roll Dice</a>
    {{/link-to}}
</ul>

Díky tomu naše záhlaví vypadá následovně:

Tento nový odkaz pak uživatele zavede na trasu „/roll“, kterou jsme právě nastavili, přesně podle přání.

Vytváření modulárních komponent

Pokud aplikaci skutečně otestujete, všimnete si jednoho problému. Otevření domovské stránky a návštěva odkazu „/roll“ funguje, ale štítky ve formuláři nejsou správně zarovnány. Je to proto, že Materialize potřebuje spustit nějaký JavaScript, aby se věci vyřešily, ale dynamické směrování znamená, že se stránka nenačítá znovu. Tady budeme muset trochu pomoci.

Zadejte komponenty. Komponenty jsou části uživatelského rozhraní, které mají celý životní cyklus a lze s nimi pracovat. Jsou také způsobem, jakým vytvoříte znovu použitelné prvky uživatelského rozhraní, pokud to budete potřebovat – uvidíme později.

Prozatím vytvoříme jedinou komponentu představující formu Roll Dice. Jako vždy se generování komponenty provádí pomocí našeho nástroje ember-cli následovně:

$ ember generate component roll-dice
installing component
  create app/components/roll-dice.js
  create app/templates/components/roll-dice.hbs
installing component-test
  create tests/integration/components/roll-dice-test.js

To nám dalo:

  • app/components/roll-dice.js – Kód, který komponentu napájí
  • app/templates/components/roll-dice.hbs – Šablona, ​​která určuje, jak bude vypadat
  • tests/integration/components/roll-dice-test.js – Test, který zajistí, že komponenta funguje správně

Všechny naše značky nyní přesuneme do komponenty – což nijak neovlivní způsob, jakým aplikace přímo funguje, ale za chvíli nám to usnadní.

Aktualizujte app/templates/components/roll-dice.hbs číst následovně:

<form class="col s12">
    <div class="row">
        <div class="input-field col s12">
            <input placeholder="Name" id="roll_name" type="text" class="validate">
            <label for="roll_name">Name of Roll</label>
        </div>
    </div>
    <div class="row">
        <div class="input-field col s6">
            <input placeholder="Number of dice" id="number_of_dice" type="number" class="validate" value="1">
            <label for="number_of_dice">Number of Dice</label>
        </div>
        <div class="input-field col s6">
            <input placeholder="Number of sides" id="number_of_sides" type="number" class="validate" value="6">
            <label for="number_of_sides">Number of Sides</label>
        </div>
    </div>
    <div class="row">
        <button class="btn waves-effect waves-light" type="submit" name="action">
            Roll Dice
            <i class="material-icons right">send</i>
        </button>
    </div>
</form>

A poté aktualizujte app/templates/roll.hbs následovně:

<div class="row">
    {{roll-dice}}
</div>

{{outlet}}

Šablona pro naši komponentu je přesně to označení, které jsme dříve měli v naší trase, a naše trasa je nyní výrazně jednodušší. roll-dice tag je to, co říká Ember, aby vykreslil naši komponentu na správném místě.

Pokud bychom to spustili nyní, neviděli bychom vůbec žádný funkční rozdíl, ale náš kód je tímto způsobem o něco modulárnější. Využijeme komponentu k opravě naší chyby při vykreslování a k přidání některých funkcí do našeho systému.

Životní cyklus komponenty

Komponenty Ember mají definovaný životní cyklus, který dodržují, s řadou háčků, které lze spustit v různých fázích. Budeme používat didRender hook, který je volán po vykreslení komponenty – ať už poprvé nebo kdykoli později – a požádá Materialize o aktualizaci popisků v textových polích.

To se provádí aktualizací kódu za komponentou, který se nachází v app/components/roll-dice.js , aby vypadal takto:

/* global Materialize:false */
import Ember from 'ember';

export default Ember.Component.extend({
    didRender() {
        Materialize.updateTextFields();
    }
});

Nyní, pokaždé, když navštívíte cestu „/roll“ – ať už je to přímým odkazem na ni nebo pomocí našeho odkazu v záhlaví – se spustí tento kód a Materialize aktualizuje štítky tak, aby fungovaly správně.

Vazba dat

Chceme také být schopni získávat data do az našeho uživatelského rozhraní prostřednictvím naší komponenty. Toho lze dosáhnout pozoruhodně snadno, ale průvodce Ember to překvapivě nepokrývá, takže to vypadá obtížněji, než by mělo být.

Každá část dat, se kterou chceme interagovat, existuje ve třídě Component jako její vlastní pole. Potom použijeme některé pomocníky k vykreslení našich vstupních polí na naší komponentě, kteří dělají práci navázání těchto vstupních polí na proměnné komponenty, takže s nimi můžeme přímo interagovat, aniž bychom se kdy museli zabývat aktivitami DOM.

V tomto případě máme tři pole, takže musíme přidat následující tři řádky do app/components/roll-dice.js , přímo v definici komponenty:

    rollName: '',
    numberOfDice: 1,
    numberOfSides: 6,

Poté aktualizujeme naši šablonu, aby se vykreslovala pomocí pomocníků namísto přímého vykreslování značek HTML. Chcete-li to provést, nahraďte <input> takto:

<div class="row">
    <div class="input-field col s12">
        <!-- This replaces the <input> tag for "roll_name" -->
        {{input placeholder="Name" id="roll_name" class="validate" value=(mut rollName)}}
        <label for="roll_name">Name of Roll</label>
    </div>
</div>
<div class="row">
    <div class="input-field col s6">
        <!-- This replaces the <input> tag for "number_of_dice" -->
        {{input placeholder="Number of dice" id="number_of_dice" type="number" class="validate" value=(mut numberOfDice)}}
        <label for="number_of_dice">Number of Dice</label>
    </div>
    <div class="input-field col s6">
        <!-- This replaces the <input> tag for "number_of_sides" -->
        {{input placeholder="Number of sides" id="number_of_sides" type="number" class="validate" value=(mut numberOfSides)}}
        <label for="number_of_sides">Number of Sides</label>
    </div>
</div>

Všimněte si, že value atribut má trochu zvláštně vypadající syntaxi. Tuto syntaxi lze použít pro jakýkoli atribut ve značce, nejen pro value . Existují tři způsoby, jak to lze použít:

  • Jako řetězec v uvozovkách – hodnota se použije tak, jak je
  • Jako řetězec bez uvozovek – hodnota je vyplněna z tohoto kusu dat komponenty, ale komponenta se nikdy neaktualizuje
  • Jako (mut <name>) – hodnota je naplněna z této části dat o komponentě a komponenta je mut při změně hodnoty v prohlížeči

Vše výše uvedené znamená, že nyní můžeme přistupovat k těmto třem polím, která jsme definovali v naší komponentě, jako by to byly hodnoty našich vstupních polí, a Ember zajišťuje, že vše takto funguje správně.

Akce součástí

Další věc, kterou chceme udělat, je interakce s komponentou. Konkrétně by bylo dobré zvládnout, když se klikne na naše tlačítko „Hoďte kostkou“. Ember to řeší pomocí akcí – což jsou části kódu ve vaší komponentě, které lze připojit k vaší šabloně. Akce jsou jednoduše definovány jako funkce v naší třídě komponent, uvnitř speciálního pole nazvaného actions , které implementují naši požadovanou funkcionalitu.

Prozatím jednoduše řekneme uživateli, co chce dělat, ale ve skutečnosti nedělá nic – to přijde na řadu. Toto použije Při odeslání akci na samotném formuláři, což znamená, že se spustí, když kliknou na tlačítko nebo stisknou Enter v jednom z polí.

Blok kódu našich akcí uvnitř app/components/roll-dice.hbs bude vypadat takto:

    actions: {
        triggerRoll() {
            alert(`Rolling ${this.numberOfDice}D${this.numberOfSides} as "${this.rollName}"`);
            return false;
        }
    }

Vrátíme false aby se zabránilo bublání událostí. Toto je poměrně standardní chování v aplikacích HTML a je v tomto případě nezbytné, aby se zabránilo opětovnému načítání stránky při odesílání formuláře.

Všimněte si, že odkazujeme na naše pole, která jsme dříve definovali pro přístup k vstupním polím. Zde není vůbec žádný přístup DOM – vše je pouze v interakci s proměnnými JavaScriptu.

Teď to jen musíme zapojit. V naší šabloně musíme značce formuláře sdělit, že musí tuto akci spustit, když onsubmit událost je spuštěna. Toto je pouze přidání jediného atributu do značky formuláře pomocí pomocníka Ember, který jej připojí k naší akci. V app/templates/components/roll-dice.hbs to vypadá následovně :

<form class="col s12" onsubmit={{action 'triggerRoll'}}>

Nyní můžeme po vyplnění formuláře kliknout na tlačítko a zobrazit vyskakovací okno s upozorněním, které nám řekne, co jsme udělali.

Správa dat mezi klientem a serverem

Další věc, kterou chceme udělat, je vlastně hodit kostkou. To bude vyžadovat určitou komunikaci se serverem – protože server je zodpovědný za házení kostkou a zapamatování si výsledků.

Náš požadovaný tok je zde:

  • Uživatelé určí kostkou, kterou chtějí hodit
  • Uživatel stiskne tlačítko „Hoďte kostkou“
  • Prohlížeč odešle podrobnosti na server
  • Server hodí kostkou, zapamatuje si výsledek a odešle výsledky zpět klientovi
  • Prohlížeč zobrazuje výsledky hodu kostkou

Zní to jednoduše. A samozřejmě s Ember to tak opravdu je.

Ember to řeší pomocí vestavěného konceptu obchodu s modely. Obchod je jediným zdrojem znalostí v celé aplikaci a každý model je jedinou informací v obchodě. Všechny modely vědí, jak se udržet v backendu, a Store ví, jak vytvářet modely a přistupovat k nim.

Předávání řízení z komponent na trasy

V celé naší aplikaci je důležité udržovat správné zapouzdření. Trasy (a řadiče, které jsme nepokryli) mají přístup do obchodu. Komponenty ne.

Důvodem je to, že trasa představuje konkrétní část funkce ve vaší aplikaci, zatímco součást představuje malou část uživatelského rozhraní. Aby s tím mohla komponenta pracovat, má schopnost vyslat signál do hierarchie, že se stala nějaká akce – velmi podobným způsobem, jakým by naše komponenty DOM mohly signalizovat naší komponentě, že se něco stalo.

Nejprve tedy přesuneme naši logiku pro zobrazení výstražného pole do trasy místo komponenty. Abychom to mohli udělat, musíme změnit následující oblasti kódu:

V logice naší trasy – app/routes/roll.js – pro registraci akce, kterou se chystáme provést, musíme přidat následující blok.

actions: {
    saveRoll: function(rollName, numberOfDice, numberOfSides) {
        alert(`Rolling ${numberOfDice}D${numberOfSides} as "${rollName}"`);
    }
}

V logice naší komponenty – app/components/roll-dice.js – potřebujeme spustit akci na naší komponentě, když jsme sami spuštěni. To se provádí pomocí sendAction mechanismus uvnitř našeho již existujícího ovladače akcí.

triggerRoll() {
    this.sendAction('roll', this.rollName, this.numberOfDice, this.numberOfSides);
    return false;
}

A nakonec musíme akci zapojit. To se provádí v šabloně pro trasu – app/templates/roll.hbs – změnou způsobu vykreslování naší komponenty:

{{roll-dice roll="saveRoll" }}

Toto sděluje komponentě, že vlastnost roll je propojen s akcí saveRoll uvnitř naší trasy. Tento název roll se pak používá uvnitř naší komponenty k označení volajícího, že byl proveden hod kostkou. Tento název dává naší komponentě smysl – protože ví, že požaduje provedení hodu kostkou, ale nezajímá ho, jak to udělá druhý kód nebo co udělá s informacemi.

Opět platí, že spuštění nezpůsobí žádný funkční rozdíl v naší aplikaci, ale znamená to, že všechny části jsou na správném místě.

Přetrvání v obchodě

Než budeme schopni uložit data do našeho úložiště, musíme definovat model, který je bude reprezentovat. To se provádí opětovným použitím našeho spolehlivého nástroje ember-cli k vytvoření struktury a jejímu naplnění.

Pro vytvoření třídy modelu provedeme:

$ ember generate model roll
installing model
  create app/models/roll.js
installing model-test
  create tests/unit/models/roll-test.js

Pak řekneme našemu modelu o atributech, kterým musí porozumět. To se provádí úpravou app/models/roll.js vypadat takto:

import DS from 'ember-data';

export default DS.Model.extend({
    rollName: DS.attr('string'),
    numberOfDice: DS.attr('number'),
    numberOfSides: DS.attr('number'),
    result: DS.attr('number')
});

DS.attr volání definují nový atribut zadaného typu – tzv. Transform in Ember. Výchozí možnosti jsou zde „řetězec“, „číslo“, „datum“ a „logická hodnota“, i když v případě potřeby můžete definovat své vlastní.

Nyní to můžeme skutečně použít k vytváření nebo rolování. To se provádí přístupem do obchodu z naší akce, kterou nyní máme v app/routes/roll.js :

saveRoll: function(rollName, numberOfDice, numberOfSides) {
    let result = 0;
    for (let i = 0; i < numberOfDice; ++i) {
        result += 1 + (parseInt(Math.random() * numberOfSides));
    }

    const store = this.get('store');
    // This requests that the store give us an instance of our "roll" model with the given data
    const roll = store.createRecord('roll', {
        rollName,
        numberOfDice,
        numberOfSides,
        result
    });
    // This tells our model to save itself to our backend
    roll.save();
}

Když to vyzkoušíme, uvidíme, že stiskneme naše Hození kostkou způsobí uskutečnění síťového volání na náš server. To se nezdaří, protože náš server to ještě neočekává, ale jde to.

Nezaměřujeme se zde na backend, takže se tím budeme zabývat. Pokud potřebujete vyvinout aplikaci Ember zcela bez backendu, pak existují možnosti – například adaptér ember-localstorage-adaptér, který bude zcela fungovat v prohlížeči. Případně stačí napsat příslušný server a zajistit, aby server a klient byli správně hostováni a vše bude fungovat.

Načítání z obchodu

Nyní, když máme nějaká data do našeho obchodu, musíme je znovu dostat ven. Zároveň napíšeme indexovou cestu – tu, která se používá při přístupu na domovskou stránku.

Ember má implicitně cestu nazvanou index který se používá k vykreslení úvodní stránky aplikace. Pokud soubory pro tuto cestu neexistují, nevyvolá se žádná chyba, ale místo toho se nic nevykreslí. Tuto cestu použijeme k vykreslení všech historických rolí z našeho obchodu.

Protože trasa indexu již implicitně existuje, není třeba používat nástroj ember-cli – můžeme přímo vytvářet soubory a je již zapojeno.

Náš obslužný program trasy přejde na app/routes/index.js a bude vypadat následovně:

import Ember from 'ember';

export default Ember.Route.extend({
    model() {
        return this.get('store').findAll('roll');
    }
});

Zde má naše trasa přímý přístup do obchodu a může použít findAll metoda pro načtení každého hodu, který byl perzistentní. Tyto pak poskytneme šabloně pomocí model metoda.

Naše šablona pak přejde do app/templates/index.hbs takto:

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Dice Rolled</th>
            <th>Result</th>
        </tr>
    </thead>
    <tbody>
    {{#each model as |roll|}}
        <tr>
            <td>{{roll.rollName}}</td>
            <td>{{roll.numberOfDice}}D{{roll.numberOfSides}}</td>
            <td>{{roll.result}}</td>
        </tr>
    {{/each}}
    </tbody>
</table>


{{outlet}}

To může přistupovat k modelu přímo z trasy a poté přes něj iterovat a vytvářet řádky tabulky. Bude to vypadat následovně:

Přehled

V tuto chvíli, po relativně malé práci, jsme vyvinuli aplikaci, která nám umožní házet kostkami a vidět historii všech hodů. To zahrnuje vazbu dat z našeho formuláře, uložení dat do úložiště a jejich zpětné načtení, podporu šablon pro zobrazení všech stránek a úplné směrování adres URL. Tuto aplikaci lze vyvinout od nuly za méně než hodinu.

Používání Ember může výrazně zlepšit efektivitu, se kterou vyvíjíte svůj frontend. Na rozdíl od knihoven, jako je React, vám Ember poskytuje celou sadu funkcí nezbytných k vytvoření plně funkční aplikace bez potřeby dalších nástrojů. Přidání ember-cli a nastavení přímo z krabice to pak posouvá na další úroveň, díky čemuž je proces neuvěřitelně jednoduchý a bezbolestný od začátku do konce. Ve spojení s podporou komunity není téměř nic, čeho by se nedalo dosáhnout.

Bohužel může být obtížné začlenit Ember do existujícího projektu. Nejlépe to funguje při zakládání nového projektu. Zabudování do stávajícího může být obtížné nebo nemožné. Ember také pracuje s velmi specifickým způsobem práce s backendy, a pokud to váš stávající backend nesplňuje, můžete strávit spoustu času a úsilí buď přepracováním backendu, nebo hledáním/zápisem pluginů. mluvit se stávajícím.

Ember má velkou sílu a může vám umožnit velmi rychle vytvářet plnohodnotné aplikační frontendy. Způsob, jakým musíte navrhnout svůj kód, vyžaduje mnoho struktury, ale často je to méně omezující, než se na první pohled zdá, protože tato struktura je tak jako tak nezbytná.