6. května 2013

Dart: Web Components

Vývojář obvykle ovládá několik jazyků, kterým je schopen řešit okruh problémů, s nimiž se běžně (a někdy i výjimečně) setkává. Co ho ale vede ke studiu dalšího jazyka? Tím něčím bývají tzv. killer-features. A web components jsou takovou killer-feature.

 

Co jsou to web components

Ve větším projektu se často vyskytují (relativně samostatné) vizuální objekty, které jsou využity opakovaně na mnoha místech. Mohou to být různé formuláře, tabulky, seznamy, atd.

Používáte-li tradiční přístup, HTML Vám práci příliš neusnadní. Možností je vytvořit komponentu v javascriptu, což velmi komplikuje vývoj, nebo je vytvářet pomocí šablonovacích systémů na straně serveru, což použití javascriptu stejně neeliminuje.

Výsledkem je komplikovaný systém, v němž jsou šablony vytvořeny směsí HTML nebo XML, CSS, javascriptu a dalšího jazyka pro tvorbu šablon (založeného obvykle ještě na server-side skriptovacím jazyce). Tvorba webové stránky pak klade velké nároky na vývojáře nebo na spolupráci v rámci týmu, pokud jsou úkoly rozděleny mezi více vývojářů.

Jazyk Dart přináší nativní podporu pro tvorbu webových komponent.

V dalším textu si ukážeme, jak vytvořit jednoduchou webovou komponentu a jak ji použít (což je mnohem komplikovanější a mnohem méně zdokumentované). Tou komponentou bude objekt pro logování událostí, k nimž na stránce dochází.

 

Komponenta LoggerComponent

Naše komponenta bude zaznamenávat dva údaje: čas výskytu události a text.
Zaznamenání se provede voláním metody log().
Vizuálně bude komponenta zděděna od HTML elementu.

Celé to představení předvedeme v IDE Eclipse. (V Dart Editoru je to prakticky totéž.)

Začneme založením projektu:
File > New nebo Alt + Shift + N, pak vybereme Dart project:

Dále vyplníme jméno projektu a zvolíme, že budeme používat web_ui:
 (Volbu Create sample content ponecháme zapnutou, zajistí import potřebných knihoven.)

Po nezbytných přípravných krocích se konečně dostáváme k vlastní komponentě. Vytvoříme nový soubor (v adresáři web v našem projektu), který pojmenujeme Logger_Component.html.

Struktura souboru je následující:

<!DOCTYPE html >

<html>
<body>
  <element name="x-logger" constructor="LoggerComponent" extends="div">
    <template>
      <!-- content -->
    </template>
    <script type="application/dart">
      // content
    </script>
  </element>
</body>  
</html>

Prvním důležitým bodem je název komponenty specifikovaný v atributu name tagu element. Ten musí začínat řetězcem "x-".
Atribut constructor obsahuje název třídy s implementací komponenty. A atribut extends udává název HTML elementu, od něhož naši komponentu odvozujeme (dědíme).

Nyní je třeba navrhnout rozhraní třídy. V logu chceme uvést čas výskytu události When a popis události What. K zápisu události bude sloužit metoda log(), která uloží uvedené informace do seznamu. Implementace třídy je triviální:

<script type="application/dart">
      import 'package:web_ui/web_ui.dart';
      import 'package:intl/intl.dart';
      import 'package:web_ui/watcher.dart' as watchers;
      
      class LoggerComponent extends WebComponent {
        List messages;
        
        LoggerComponent() {
          messages = new List();
        }
        
        void log(txt) {
          LogRecord logRec = new LogRecord(new DateTime.now(), txt);
          messages.add(logRec);
          
          // This is necessary if there is a chance the component will be changed outside of events
          watchers.dispatch();
        }
      }

Za pozornost stojí metoda log(). Jednak v ní využíváme další třídu LogRecord (uvedena níže), jejíž instance vkládáme do připraveného seznamu, jednak po aktualizaci seznamu zpráv na samém konci metody zajišťujeme překreslení komponenty. Tento krok je víceméně nedokumentovaný, proto na něj explicitně upozorňuji.
Doplníme ještě zmiňovanou třídu LogRecord:

class LogRecord {
        var When;
        var What;
        
        LogRecord(DateTime w, String msg) {
          var fmt = new DateFormat("yyyy-MM-dd hh:mm:ss");
          When = fmt.format(w);
          What = msg;
        }
      }
    </script>

Zde upozorním jen na formátování data a času pomocí třídy DateFormat z balíčku 'intl/intl.dart'.

Dále se budeme věnovat šabloně.

<template>
      <table class="LoggerComponent">
        <thead>
          <tr>
            <th>#</th>
            <th>Time</th>
            <th>Event description</th>
          </tr>
        </thead>
        <tbody template iterate="item in messages">
          <tr>
            <td style="text-align: right">{{$index + 1}}</td>
            <td>{{item.When}}</td>
            <td>{{item.What}}</td>
          </tr>
        </tbody>
      </table>
    </template>

Tvorba šablon je poměrně dobře zdokumentovaná snad jen s výjimkou $index, což je proměnná udávající pořadí iterace (počítáno od nuly).

Poznámka: Atribut template (zde použit v tagu tbody) je v současné verzi jazyka Dart povinný, nicméně se předpokládá, že v budoucích verzích bude odstraněn.

Tím je koponenta kompletní. Zbývá ji použít v našem projektu.

Použití komponenty


Začneme úpravou "hlavního" HTML souboru (v mém případě se jmenuje dartwebuidemo.html).

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>Sample app</title>
    <link rel="stylesheet" href="dartwebuidemo.css">
    <link rel="stylesheet" href="menu.css">
    <link rel="stylesheet" href="logger_component.css">
    
    <link rel="import" href="logger_component.html">
  </head>
  <body on-click="onMouseClick($event)">
    <h1>DartWebUIDemo</h1>
    
    <div id="menu_container">
      <ul class="Menu">
        <li>Menu item 1</li>
        <li>Menu item 2</li>
        <li>Menu item 3</li>
      </ul>  
    </div>
    
    <h3>Event log</h3>
    <x-logger id="logger"></x-logger>

    <script type="application/dart" src="dartwebuidemo.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html> 


Za povšimnutí stojí zejména vložení komponenty do stránky prostým použitím tagu pojmenovaného tak, jak bylo uvedeno v atributu name v definici komponenty. Soubor komponenty je předtím nutné importovat do HTML souboru pomocí tagu link.
Dalším místem, kterého si povšimneme, je definice obsluhy události click v tagu body. Obsluha této události bude zapisovat záznamy do logu. Můžete samozřejmě otestovat i jinou událost - např. mouseover generuje pozoruhodné množství záznamů. :-)
Jinak stránka obsahuje nějaké další prvky, které slouží pouze pro účely předvedení komponenty v akci.

A zbývá poslední zajímavá část, kterou je implementace obsluhy výše uvedené události. Tu zapíšeme do souboru uvedeného na konci hlavního HTML souboru, tedy do dartwebuidemo.dart.

import 'dart:html';
import 'package:web_ui/web_ui.dart';

const LOGGER_ID = "#logger";

void main() {
  // This gets called after the DOM is completely loaded.
  // Component objects are not available at this moment though.
}

onMouseClick(MouseEvent e) {
  Element el = e.target;
  var logger = document.query(LOGGER_ID).xtag;
  logger.log(el.tagName + " clicked");
}

Zde opět stručně upozorním na způsob získání DOM objektu komponenty, tedy na použití vlastnosti xtag. V současné verzi je toto jediný správný způsob, nicméně se očekává, že v budoucích verzích nebude vlastnost xtag nutná (a nebude ani existovat).

Co by to bylo za komponentu, kdyby nebylo možné ji opakovaně použít.
Jak? Prostě vezmeme soubor s komponentou (logger_component.html), nakopírujete jej do nového projektu a komponentu použijete stejně snadným způsobem, jako jsme si ukázali v tomto článku.

(Překládaný příklad je natolik jednoduchý, že sem nedávám projekt ke stažení. Kdyby byl zájem, stačí napsat komentář pod článek.)

Žádné komentáře: