Vytváření ambiciózních webových aplikací s Ember.js

Ember.js je solidní jednostránkový aplikační rámec pro vytváření moderních webových aplikací.

Než Angular a React dosáhly kritického množství, vedl Ember.js ve vytváření škálovatelných jednostránkových aplikací. I když se pozornost možná posunula, Ember.js zůstává skvělou a životaschopnou možností pro vývojáře, kteří chtějí prototypovat a budovat moderní klienty front-end.

Ember je díky své vyspělosti skvělou alternativou k Angular 2. Ember CLI bylo dokonce použito jako inspirace pro vytvoření Angular 2 CLI, aby vývojářům pomohlo efektivněji vytvořit lešení jejich aplikací. Ve srovnání s Reactem nabízí Ember o něco více ihned po vybalení, jako je směrování a dobře definovaná struktura modelu.

V dnešním tutoriálu vytvoříme jednostránkovou aplikaci s Ember.js 2.x. Ember.js 2.x, stejně jako Angular 2, je spíše framework a může se pochlubit 10násobným zlepšením výkonu oproti iteraci 1.x. Pro náš backend místo budování a nasazování tradičního webového serveru vytvoříme webovou úlohu, která bude poskytovat funkce na straně serveru. Začněme se scénou.

Vytvoření back-endu pomocí Webtask

Aplikace, kterou vytvoříme pomocí Ember.js, bude crowdsourcingovou aplikací pro události. Správci aplikace vytvoří události, o kterých mohou uživatelé platformy hlasovat. Každá událost bude mít určitý požadavek na hlasy potřebné k tomu, aby se tato událost mohla uskutečnit. Události budou moci zobrazit všichni, ale hlasovat o událostech budou moci pouze ověření uživatelé. Začneme vytvořením našeho backendu poháněného Webtask.

Webtask je platforma bez serveru, fungující jako služba, vyvinutá společností Auth0, která umožňuje vývojářům vytvářet mikroslužby a zpřístupňovat je prostřednictvím HTTP. Pokud ještě nemáte účet Webtask, můžete si jej zdarma zaregistrovat zde. Jakmile se zaregistrujete, budete muset do počítače nainstalovat rozhraní Webtask CLI spuštěním npm install wt-cli -g . Node.js a NPM jsou jediné potřebné předpoklady.

Jakmile máte nainstalované rozhraní Webtask CLI, spusťte wt-cli init a budete požádáni o e-mail nebo telefonní číslo. Zadejte jeden z nich a obdržíte potvrzovací kód, který budete muset zadat do CLI, abyste dokončili proces ověřování. Jakmile to uděláte, jste připraveni psát a nasazovat webové úlohy.

Existuje mnoho různých přístupů k psaní webových úloh. Můžeme napsat webovou úlohu, která plní jedinou funkci, například poslat uvítací e-mail, když se uživatel zaregistruje, nebo můžeme napsat celou aplikaci v rámci webové úlohy. Rozhodneme se pro druhou možnost a sestavíme celou naši backendovou implementaci s jediným webovým úkolem. Chceme se zaměřit na Ember.js, takže rychle projdeme tuto sekci. Více o tom, jak Webtask funguje, se vždy můžete dozvědět v dokumentech.

Náš webový úkol bude mít čtyři trasy. /events route vrátí seznam všech dostupných událostí, /events/:id route vrátí jedinou událost, /events/:id Cesta PUT zvýší počet hlasů a nakonec cesta „/seed“ zasévá naši databázi (úložiště webových úloh) několika počátečními událostmi, o kterých se bude hlasovat.

Podívejte se na naši implementaci backendu v kódu níže. Webtask, který vytváříme, bude velmi podobný tradiční aplikaci Express.js s nějakým specifickým kódem Webtask. Pokud některý z konceptů nedává smysl nebo byste chtěli nějaké další zdroje, prohlédněte si dokumentaci k Webtask, konkrétně o úložišti a ověřování, kde se dozvíte více. Tento kód uložíme do souboru s názvem api.js .

// Get our dependencies
var app = new (require('express'))();
var wt = require('webtask-tools');

// Define the events route which will retrieve a list of all events
app.get('/events', function(req, res){
    req.webtaskContext.storage.get(function (error, data) {
        if (error) return res.send(error);
        res.json({event: data});
    });
})

// Return a specific event based on the event ID attribute
app.get('/events/:id', function(req,res){
  req.webtaskContext.storage.get(function(error, data){
    if(error) return res.send(error);
    for(var i = 0; i < data.length; i++){
      if(req.params.id == data[i].id){
        res.json({event : data[i]})
      }
    }
  })
})

// The PUT request to the events/:id route will increment the vote count of the particular event
app.put('/events/:id', function(req, res){
  req.webtaskContext.storage.get(function(error, data){
    for(var i = 0; i < data.length; i++){
      if(req.params.id == data[i].id){
        data[i].votes += 1;
        req.webtaskContext.storage.set(data, function(err){
          res.json({status: 'ok'})
        })
      }
    }
  })
})

// Once our Webtask is live, we'll hit this route once, to seed our event database
app.get('/seed', function(req, res){
  req.webtaskContext.storage.get(function (error, data) {
    if (error) return cb(error);
      data = events();
      req.webtaskContext.storage.set(data, function (error) {
        if (error) return cb(error);
        res.json({status:'ok'});
      });
  });
})

module.exports = wt.fromExpress(app)

// This function will return our seed data.
function events(){
  return [
    {
      id: 10432,
      name : "Meet and Greet: Kobe Bryant",
      description: "See the legendary Kobe Bryant talk about his career with the NBA and how he became one of the greatest players of all time.",
      img : "",
      votes: 10289,
      required: 25000,
      featured: true
    },
    {
      id: 14582,
      name : "Marvel vs. DC at San Diego Comic Con",
      description: "Watch the battle between the greatest superheros at Comic Con 2017.",
      img : "",
      votes: 14900,
      required: 20000,
      featured: false
    },
    {
      id: 42000,
      name : "AMA: Elon Musk",
      description: "Ask Elon Musk anything. The CEO of Tesla and Space X has agreed to answer any and all of your questions.",
      img : "",
      votes: 10289,
      required: 10000,
      featured: false
    },
    {
      id: 54200,
      name : "Secret Event",
      description: "This could be anything. Vote to find out!!!",
      img : "",
      votes: 4754,
      required: 7500,
      featured: false
    },
    {
      id: 55900,
      name : "Meet the Developers",
      description: "Meet the developers building this awesome platform.",
      img : "",
      votes: 5900,
      required: 5000,
      featured: false
    },
  ]
}

Po implementaci jsme připraveni nasadit náš backend. Spusťte wt-cli create api.js příkaz v adresáři, kde máte nově vytvořený api.js soubor zůstane a během několika sekund bude vytvořen a nasazen váš webový úkol. Přejděte na adresu URL uvedenou v CLI a uvidíte, že váš kód běží. První koncový bod, na který byste měli přejít, by měl být /seed route, protože to osévá vaše úložiště Webtask některými událostmi. Zatím je vše dobré. Pojďme k budování našeho front-endu.

Budování našeho SPA pomocí Ember.js

Ember.js byl průkopníkem v používání rozhraní příkazového řádku (CLI) pro snadné lešení a pomoc při vývoji webových aplikací. Při vytváření naší aplikace využijeme CLI. Chcete-li nainstalovat CLI, spusťte npm install ember-cli -g příkaz. Node.js a NPM jsou opět předpoklady pro získání CLI. Jakmile je CLI nainstalováno, jste připraveni začít sestavovat aplikaci.

Chcete-li vytvořit novou aplikaci Ember.js, spusťte ember new a název aplikace. Spustíme ember new events-app . Ember automaticky vytvoří nový adresář s názvem events-app , lešení základní aplikační struktury a získejte všechny potřebné závislosti. Dokončení může trvat několik minut. Po dokončení instalace přejděte do adresáře events-app zadáním cd events-app a stisknutím klávesy Return na klávesnici.

Abychom se ujistili, že naše aplikace byla správně inicializována, spusťte ember server a přejděte na localhost:4200 ve vašem prohlížeči. Pokud uvidíte zprávu „Gratulujeme, že jste to zvládli!“, můžete jít. Pokud ne, doporučil bych znovu spustit ember new protože NPM a Bower někdy nemusí správně stáhnout všechny závislosti.

Lešení aplikace tímto způsobem nám poskytuje velmi základní výchozí bod skvělý pro vytváření aplikací od začátku. V tomto tutoriálu máme ambiciózní cíl a hodně půdy, kterou musíme pokrýt, takže k pokračování našeho vývoje použijeme jiný startovací projekt. Při přidávání ověřování uživatelů do naší aplikace použijeme projekt Auth0 Ember.js Quickstart seed. Mnohá ​​z témat, kterými se budeme zabývat, by byla úplně stejná, jako kdybyste pokračovali z lešení, které jsme vytvořili dříve, ale to nám umožní postupovat o něco rychleji.

Přejděte na Auth0 Ember.js 2 Quickstart a stáhněte si poskytnutý seed projekt. Abyste mohli implementovat funkci ověřeného uživatele, budete si muset zaregistrovat účet Auth0, takže pokud ještě nemáte účet, můžete si jej zaregistrovat zde. Jakmile si stáhnete rychlý start, otevřete adresář a spusťte npm install následuje bower install . Tím se stáhne a nainstaluje všechny závislosti, které budeme potřebovat. S nainstalovanými závislostmi spusťte ember server a přejděte na localhost:4200 zobrazíte výchozí aplikaci pro rychlý start.

Zatím nic moc. Nejprve nakonfigurujeme některá nastavení prostředí a poté se pustíme do vytváření aplikace. Otevřete environement.js soubor umístěný v adresáři config. V tomto souboru přejděte na atribut s názvem Auth0 a změňte clientID a domain nastavení vašeho Auth0 ClientID a domény. Tyto hodnoty najdete na řídicím panelu Auth0.

Pokud máte server Ember stále spuštěný, všimnete si, že jakékoli provedené změny se projeví v reálném čase. To je další výhoda vytváření aplikace pomocí CLI. ember server příkaz spustí live-sync a sleduje vaši aplikaci, kdykoli je provedena změna, server se automaticky restartuje.

Nyní máme docela dobré lešení naší aplikace. Dále budeme chtít přidat knihovnu Bootstrap CSS, abychom mohli snadno stylizovat naši aplikaci. Otevřete index.html a do sekce head přidejte knihovnu Bootstrap 3 z CDN. Získáme Bootstrap z MaxCDN a přidáme následující do našeho index.html stránka:<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" > . Když se nyní podíváte na svou aplikaci, všimnete si, že vypadá mnohem lépe.

Pojďme do toho a otevřeme application.hbs soubor další. Upravte obsah application.hbs soubor takto:

{{outlet}}

Prozatím budeme mít soubor obsahovat pouze jeden řádek {{outlet}} . Pokud jste již dříve pracovali s předchozími SPA frameworky, možná již víte, k čemu to je, ale pokud ne, zde zobrazíme komponenty založené na našem routeru. Pojďme si vytvořit trasu, abychom viděli, jak tato funkce funguje. {{main-navigation}} součást, kterou jsme odebrali, zobrazovala naši nejlepší navigaci. Vrátíme se k tomu později.

V okně terminálu zadejte následující příkaz ember generate route events a stiskněte enter. Tento příkaz nám vytvoří několik různých souborů. První bude events.js soubor, kam můžeme přidat naši front-end logiku pro trasu událostí. Dále events.hbs soubor pro naši šablonu a nakonec se Ember CLI postaralo o přidání trasy událostí do našeho routes.js soubor.

Prozatím pokračujte a otevřete events.hbs soubor a přidejte název na naši stránku. Přidejte následující kód:<h1>Events</h1> . Uložte soubor a přejděte na localhost:4200/events . Zobrazí se váš titul. Zatím je to dobré.

Dalším konceptem, který bych vám rád představil, jsou komponenty. Komponenty v Ember.js nám umožňují vytvářet opakovaně použitelné úryvky funkcí. Přidáme komponentu, která zobrazí podrobnosti o každé z našich událostí. Chcete-li vytvořit komponentu, spusťte ember generate component app-event . Každá komponenta, kterou vytvoříte, bude muset mít pomlčku. Důvodem je kompatibilita. Pokud vytvoříte komponentu s názvem event a použijete ji jako <event></event> ve vaší aplikaci a někdy v budoucnu se W3C rozhodne implementovat značku – vaše aplikace by se pravděpodobně rozpadla. Komponenta, kterou jsme vytvořili, bude implementována o něco později, zatím budeme lešení zbytku našich tras.

Již máme trasu akcí, která zobrazí seznam všech našich akcí. Dále vytvoříme trasu pro zobrazení pouze jedné události. Spusťte ember generate route event . Jakmile to uděláte, pokračujte a otevřete router.js soubor umístěný pod app adresář. Tento soubor obsahuje data našeho routeru. Zde budeme chtít udělat dvě věci. Nejprve nastavíme výchozí trasu, která bude naší trasou událostí, poté upravíme trasu události tak, aby akceptovala parametr trasy. Podívejte se na implementaci níže:

Router.map(function() {
  // Existing Routes added by the Auth0 Quickstart
  // We'll have a template for the existing index from the quick start.
  this.route(‘index');
  // Sets the default route to events
  this.route('events', { path: '/' });
  this.route('events')
  // Changes the default /event route to /event/:id where the :id is a variable
  this.route('event', {path: '/event/:id'});
});

Již jsme měli několik tras z Auth0 Ember.js Quickstart, takže je zatím necháme tak, jak jsou, jen provedeme nějaké úpravy našich tras. Nyní, když máme definovány naše trasy, pojďme vytvořit naši aplikaci.

Začněme kořenem naší aplikace. Otevřete main-navigation.hbs soubor umístěný pod templates/components . Nahraďte stávající kód šablony řetězcem:

<nav class="navbar navbar-default navbar-fixed-top">
  <div class="container-fluid">
    <div class="navbar-header">
      {{#link-to 'index' classNames='navbar-brand'}}
        Home
      {{/link-to}}
    </div>
    <ul class="nav navbar-nav navbar-left">
      {{#link-to 'events' tagName='li'}}
        <a>Events</a>
      {{/link-to}}
    </ul>
    {{! display logout button when the session is authenticated, login button otherwise }}
    {{#if session.isAuthenticated}}
      <a {{action 'logout'}} class="btn btn-danger navbar-btn navbar-right">Logout</a>
    {{else}}
      <a href="/login" class="btn btn-success navbar-btn navbar-right">Login</a>
    {{/if}}
  </div>
</nav>

Otevřete application.hbs a přidejte {{main-navigation}} komponentu nad {{outlet}} . Zkontrolujte aplikaci, abyste se ujistili, že se nový navigační panel správně zobrazuje. Do našeho application.hbs také přidáme jednoduché zápatí soubor. Podívejte se na dokončenou implementaci níže:

{{main-navigation}}
{{outlet}}

<footer>
  <p class="text-center text-muted"><small>&copy; 2016 Events!</small></p>
</footer>

Pokud přejdete na localhost:4200 nyní uvidíte záhlaví a zápatí a také obsah zobrazené stránky, na které se nacházíte. Přidali jsme trochu funkčnosti Ember.js s kontrolou logického stavu, pojďme pokračovat ve vytváření naší aplikace. Další stránka, kterou se chystáme vybudovat, je domovská stránka a stránka událostí. Otevřete events.hbs soubor a přidejte následující kód:

<div class="jumbotron text-center">
  <h1>Events</h1>
</div>

<div class="container">
  <div class="row">
    {{#each model as |event|}}
      {{app-event event=event}}
    {{/each}}
  </div>
</div>

Otevřete app-event.hbs šablona další a přidejte následující kód:

<div class="col-sm-6">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h3 class="panel-title">{{event.name}}</h3>
    </div>
    <div class="panel-body" style="min-height: 80px;">
      {{event.description}}
    </div>
    <div class="panel-footer">
      <ul class="list-inline">
        <li><a class="btn btn-sm btn-success"><span class="glyphicon glyphicon-thumbs-up"></span> {{event.votes}}</a></li>
        <li class="pull-right">
          <a class="btn btn-sm btn-default">Required: {{event.required}}</a>
        </li>
      </ul>
    </div>
  </div>
</div>

Pojďme si trochu vysvětlit, co se děje. Když uživatel narazí na stránku událostí (nebo domovskou stránku, protože to je naše výchozí stránka). Načteme data našeho modelu a spustíme je prostřednictvím forEach smyčka na stránce událostí. Potom pro každou událost, kterou dostaneme, použijeme naše app-event.hbs šablonu a vytvořte uživatelské rozhraní pro událost předávání dat z naší stránky událostí. Pokud se nyní podíváte na svou aplikaci, uvidíte pouze záhlaví. Pojďme získat naše události z webového úkolu, který jsme vytvořili, a zobrazit je na stránce. Abychom mohli zadávat požadavky, musíme nejprve provést několik úprav našeho aplikačního adaptéru. Otevřete soubor s názvem application.js umístěný v adapters adresář. Přidejte následující kód:

// We are changing our default adapter to use a REST Adapter
export default DS.RESTAdapter.extend(DataAdapterMixin, {
  // The host will be where our Webtask lives
  host: 'YOUR-WEBTASK-URL/api',
  authorizer: 'authorizer:application',
  // We will want to add an Authorization header containing our JSON Web Token with each request to our server. We'll get to this functionality a little later, but we can configure it now.
  headers : Ember.computed(function(){
    var token = JSON.parse(localStorage.getItem('ember_simple_auth:session'));

    return {"Authorization": 'Bearer ' + token.authenticated.id_token};
  })
});

S naší sadou adaptérů otevřete events.js soubor další. Dále přidejte následující kód do events.js soubor:

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    // This will make a GET request to our webtask and get all of the events
    return this.store.findAll('event');
  }
});

Nyní, když navštívíte váš localhost:4200 nebo localhost:4200/events trasy si všimnete, že se vaše aplikace zhroutila. Ember.js neví, jak zacházet s událostmi, které vracíme. Budeme muset vytvořit model, který Ember.js řekne, jak spotřebovávat data, která získá. Chcete-li přidat model s Ember.js, spustíme ember generate model event příkaz. Dále otevřete event.js soubor umístěný v adresáři models a přidejte následující kód:

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  description: DS.attr('string'),
  votes: DS.attr('number'),
  required: DS.attr('number'),
  featured: DS.attr('boolean')
});

Náš model popisuje všechny vlastnosti, které bude mít konkrétní událost. Nyní, když přejdeme na localhost:4200 všechny naše události se zobrazí správně. I když uživatelské rozhraní vypadá trochu divně. Máme lichý počet událostí. Udělejme několik změn v našem events.hbs mít mnohem čistší uživatelské rozhraní. Ve smyčce, kde iterujeme naše události, provedeme následující úpravy:

...
{{#each model as |event|}}
      {{#if event.featured}}
        <div class="jumbotron">
          <h2>Featured</h2>
          <h1>{{event.name}}</h1>
          <p>{{event.description}}</p>
          <a class="btn btn-lg btn-primary" href="/event/{{event.id}}">View</a>
        </div>
      {{else}}
       {{app-event event=event}}
      {{/if}}
{{/each}}
...

Pokud se nyní podíváte na stránku, uvidíte vybranou událost úplně nahoře. Tohle vypadá mnohem lépe. Dále upravíme naše event.hbs a přidejte uživatelské rozhraní pro zobrazení jedné události. Náš kód zde bude velmi jednoduchý, protože znovu používáme komponentu, kterou jsme již vytvořili:

<div class="container">
  <div class="row">
      {{app-event event=model}}
  </div>
</div>

Chcete-li přidat funkci pro načtení a zobrazení jedné události, pojďme do toho a otevřete event.js soubor v našich trasách a přidejte následující:

import Ember from 'ember';

export default Ember.Route.extend({
  // We'll capture the route parameters and use the id to retrieve a single record from our Webtask that matches the id of the event
  model(params){
    return this.store.findRecord('event', params.id);
  }
});

Zatím je vše dobré. Naše aplikace se opravdu spojuje. Poslední funkcí, kterou přidáme, je možnost pro uživatele hlasovat o tom, které události by se rády staly. Chcete-li to provést, otevřeme app-event.js soubor pod components adresář. Zde přidáme akci s názvem hlasovat který uživateli umožní hlasovat pro událost. Implementace je následující:

import Ember from 'ember';

export default Ember.Component.extend({
  // We'll inject our store service
  store: Ember.inject.service(),
  actions: {
    vote: function(event) {
       var store = this.get('store');
       // We'll find the event by id and if we get an event from the Webtask, we'll increment its votes attribute by one and save the data by making a POST request to our Webtask.
       store.findRecord('event', event.id).then(function(event) {
        event.incrementProperty('votes');
        event.save();
      });
    }
  }
});

S funkčností na místě pojďme do toho a přidejte akci do naší šablony. Otevřete app-event.hbs soubor a přidejte akci {{action 'vote' event}} na naše tlačítko úspěchu. Uložte soubor a pojďme vyzkoušet funkčnost přechodem na localhost:4200 a hlasování o několika různých akcích. Měli byste vidět počítadla narůstající v reálném čase. Nechceme však, aby mohl hlasovat jen tak někdo, takže před hlasováním budeme vyžadovat ověření uživatele. Nyní implementujeme tuto poslední funkci.

Již máme docela dobrý základ pro autentizaci, protože používáme rychlý start Auth0 a již jsme provedli určitou konfiguraci, abychom zajistili rychlou implementaci našeho přihlašovacího systému. Naše implementace je vlastně celá připravena. Vše, co budeme potřebovat, je zajistit, že na našem řídicím panelu Auth0 máme localhost:4200/callback jako povolenou adresu URL zpětného volání. Jakmile budete připraveni, klikněte na tlačítko Přihlásit a buď se přihlaste, nebo se zaregistrujte. Pokud vše proběhlo v pořádku, budete přihlášeni a zelené tlačítko pro přihlášení bude nahrazeno červeným tlačítkem pro odhlášení.

Nyní se ujistíme, že pouze uživatelé, kteří jsou ověřeni, mohou odeslat požadavek na náš backend. Otevřete app-event.js . Aktuální implementaci nahradíme následujícím:

vote: function(event) {
      var store = this.get('store');
        store.findRecord('event', event.id).then(function(event) {
          event.incrementProperty('votes');
          event.save().catch(function(error){
            event.decrementProperty('votes');
            alert(‘You must be logged in to vote');
          });
      });
}

Budeme také muset provést jednu úpravu našeho webového úkolu. Otevřete api.js Webtask a přidejte následující do module.exports funkce:

...
module.exports = wt.fromExpress(app).auth0({
  clientId: function(ctx, req){return 'YOUR-AUTH0-CLIENT-ID'},
  clientSecret: function(ctx,req){return 'YOUR-AUTH0-CLIENT-SECRET'},
  domain: function(ctx,req){return 'YOUR-AUTH0-DOMAIN'},
  exclude: function (ctx, req, appPath) { return req.method === 'GET'; }
});
...

Znovu nasaďte svůj webový úkol spuštěním wt-cli deploy api.js . Po dokončení nasazení vaší webové úlohy zajistíme metodu PUT. Nyní, když je odeslán požadavek PUT na events/:id , Webtask zajistí, že požadavek je doprovázen platným JSON Web Token (JWT). Pokud ano, bude proces pokračovat, jinak webová úloha vrátí 401 Unauthorized. Zbytek tras bude nadále fungovat jako dříve a kdokoli k nim bude mít přístup. Chcete-li se dozvědět více o tom, jak jsou Webtasks ověřovány, prohlédněte si naše dokumenty.

A je to! Dnes jsme vytvořili kompletní aplikaci s nejnovější verzí Ember.js. Ukázali jsme, jak můžete snadno přidat ověřování uživatelů a chránit svůj backend pomocí Auth0. Vytvořili jsme také backend založený na Express.js s platformou Webtask bez serveru. Vím, že to bylo hodně k trávení, takže pokud máte nějaké dotazy, dejte mi prosím vědět a já se pokusím je zodpovědět. Pokud se naopak cítíte dobrodružně, proč nerozšíříte funkcionalitu Webtask a nepřidáte možnost pro administrátory vytvářet nové události a testovat, abyste viděli, co jste se naučili.


No