Strona Główna Blog webmasterski

Pierwszy komponent w Polymerze

Jest to wpis rozpoczynający serię o bibliotece Polymer, w którym pokażę jak zrobić element okna modalnego. Z racji tego, że jest to pierwszy wpis, zacznę od wyjaśnienia, w jaki sposób instaluje się bibliotekę i jak jej używać, a dopiero potem przejdę do tematu głównego, czyli kodu Custom Elementu.

Web Components – co to takiego

Zanim jednak zaczniemy używać Polymera, wypadałoby najpierw zrozumieć, czym właściwie są web componenty, bo właśnie na tej technologii bazuje Polymer. Wchodząc na stronę główną tejże biblioteki, naszym oczom ukaże się informacja:

Unlock the Power of Web Components. Polymer is a JavaScript library that helps you create custom reusable HTML elements, and use them to build performant, maintainable apps.

A zatem Polymer jest biblioteką, która umożliwia używanie Web Components już dziś, a dodatkowo upraszcza sposób ich implementacji oraz udostępnia narzędzia do budowania i testowania komponentów.

No dobra, ale dalej nie wyjaśniłem, co to są te Web Components. Jako że jestem leniwy, a poza tym jest to obszerne zagadnienie, po prostu udostępnię odnośniki do dobrych materiałów na ten temat.

Tworzenie nowego projektu

Przed utworzeniem projektu należy zainstalować Polymer CLI. Do zainstalowania wymagany jest również Bower, a zatem musimy wykonać dwie komendy:

npm install -g bower
npm install -g polymer-cli

Kiedy mamy już dostęp do komendy polymer, możemy utworzyć nowy projekt poprzez wpisanie komendy:

polymer init

Na liście wyboru należy zaznaczyć polymer-2-element, a w polu Element name podajemy nazwę naszego elementu np. polymer-modal.
W tym momencie powinna zostać utworzona struktura katalogów, jak również powinny się zainstalować zależności, natomiast w pliku polymer-modal.html (w zależności od tego, jaką podaliście nazwę) został umieszczony przykładowy kod.

Aby otworzyć komponent w przeglądarce, trzeba wykonać komendę polymer serve. Nie powinno nikogo dziwić, że po otworzeniu adresu skopiowanego z okna konsoli, wyświetli się strona z jakimś randomowym napisem – projekt na razie jest pusty.

Robimy Custom Element

Zacznę trochę nietypowo, czyli od zmodyfikowania pliku index.html, który od teraz będzie zawierał taki kod:

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="utf-8">
    <title>polymer-modal</title>
    <script src="bower_components/webcomponentsjs/webcomponents-loader.js"></script>
    <link rel="import" href="polymer-modal.html">
</head>
<body>
    <polymer-modal>
        <h1 slot="title">Nagłówek okna</h1>
        <div slot="content">
            <p>Akapit umieszczony w oknie.</p>
        </div>
        <span slot="close-label">Zamknij</span>
    </polymer-modal>
</body>
</html>

W sekcji head umieszczono dwie ważne linijki. Pierwsza z nich to skrypt webcomponents-loader.js, który jest konieczny po to, żeby nasz komponent działał w różnych przeglądarkach.
Druga linia, która odnosi się do pliku polymer-modal.html to tzw. HTML Import. Służy on, jak sama nazwa wskazuje, do zaimportowania pliku HTML, w którym będziemy trzymać kod komponentu.

W body umieściłem znacznik elementu polymer-modal, chciałbym zwrócić uwagę na nazwę znacznika, która składa się z dwóch członów rozdzielonych myślnikiem, taką konwencję nazewnictwa narzuca specyfikacja Custom Elementów.
Wewnątrz znacznika znajdują się trzy elementy, kolejno: nagłówek okna, kontener treści i etykieta przycisku zamykania (zamiast tekstu można umieścić ikonę). Kolejność znaczników nie będzie miała żadnego znaczenia, to co jest ważne to atrybut slot, o którego znaczeniu dowiedzie się potem.

Główny plik komponentu

Każdy element posiada swoje własne drzewo elementów, które jest wyodrębnione od głównego drzewa dokumentu, jest to tzw. Shadow DOM. Nasz element polymer-modal powinien być złożony z jakichś elementów składowych, można też powiedzieć, że powinniśmy utworzyć szablon HTML, aby nadać elementowi jakieś cechy, bo w przeciwnym razie będzie on jedynie pustym kontenerem.

Niemal wszystkie przeglądarki poza Internet Explorerem obsługują stosunkowo nowy znacznik <template>. Kod umieszczony w tym znaczniku nie jest renderowany, a jego zawartość można sklonować i wstawić do innego elementu. Użyjemy znacznika template, aby utworzyć zawartość naszego elementu.

Oto jak będzie wyglądać kod głównego pliku polymer-modal.html:

<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="style.html">

<dom-module id="polymer-modal">
    <template>
        <style include="polymer-modal-style"></style>
        <div class="modal">
            <button type="button" class="modal__close">
                <slot name="close-label">X</slot>
            </button>
            <div class="modal__heading">
                <slot name="title"></slot>
            </div>
            <div class="modal__content">
                <slot name="content"></slot>
            </div>
        </div>
    </template>
    <script src="polymer-modal.js"></script>
</dom-module>

Rzeczą, która od razu rzuca się w oczy, są dwa importy u góry pliku. Pierwszy z nich (polymer-element.html) jest konieczny, abyśmy mogli utworzyć „Polymerowy” Custom Element. W drugim imporcie importujemy style elementu, będąc przy stylach, od razu zwrócę uwagę, że oprócz importu, trzeba umieścić znacznik style ze specjalnym atrybutem include.

W Polymerze kod komponentu umieszczamy wewnątrz znacznika dom-module, znacznik ten posiada atrybut id, który musi być identyczny z nazwą tworzonego elementu.

W środku dom-module koniecznie musi znaleźć się szablon oraz skrypt. W skrypcie będzie umieszczona definicja klasy elementu (mój skrypt jest wydzielony do osobnego pliku).

W szablonie umieściłem trzy elementy <slot>, powinniście to skojarzyć z atrybutem slot, o którym pisałem w rozdziale Robimy Custom Element. Ten element jest bardzo przydatny, dzięki niemu inne elementy, które zostały oznaczone atrybutem slot, są wstawiane do odpowiadających im slotów na podstawie atrybutu name. Za sprawą tej właściwości elementy tj. nagłówek, treść i etykieta przycisku, będą umieszczone w odpowiednim miejscu.

Klasa elementu

Aby nasz modal mógł działać, musimy napisać klasę, która dziedziczy po Polymer.Element. Poniżej został umieszczony kod pliku polymer-modal.js.

class PolymerModalElement extends Polymer.Element {
    static get is() {
        return 'polymer-modal';
    }
    static get properties() {
        return {
            opened: {
                type: Boolean,
                value: false,
                reflectToAttribute: true
            },
        };
    }
}

window.customElements.define(PolymerModalElement.is, PolymerModalElement);

Klasa może mieć dowolną nazwę. Ja swoją nazwałem PolymerModalElement, a wewnątrz niej utworzyłem dwie metody statyczne. Pierwsza z nich, czyli metoda is jest konieczna i musi zwracać nazwę elementu. Druga metoda properties służy do definiowania atrybutów elementu.

Nasz przykładowy modal będzie najprostszym z możliwych i jedyne co będzie umieć to się otworzyć i zamknąć. Z tej właśnie przyczyny utworzyłem w nim tylko atrybut o nazwie opened, który jest typu Boolean. Zostanie on użyty do zmiany widoczności elementu.

Style elementu

Okno modalne posiada już szablon i właściwość opened, która mówi nam, czy jest one otwarte. W związku z tym posiadamy już wszystko, co jest potrzebne do nadania stylów.

<dom-module id="polymer-modal-style">
    <template>
        <style>
            :host {
                --modal-width: 400px;
                --modal-background: #fff;
                --modal-padding: 10px;
                --modal-border: 1px solid #000;
                display: none;
            }

            :host([opened]) {
                position: fixed;
                top: 0;
                left: 0;
                display: flex;
                width: 100vw;
                height: 100vh;
                overflow-y: auto;
                overflow-x: hidden;
                justify-content: center;
                align-items: center;
            }

            .modal {
                position: relative;
                top: 0;
                left: 0;
                width: var(--modal-width);
                background: var(--modal-background);
                padding: var(--modal-padding);
                border: var(--modal-border);
            }

            .modal__heading {
                border-bottom: var(--modal-border);
            }

            .modal__close {
                position: absolute;
                top: var(--modal-padding);
                right: var(--modal-padding);
            }
        </style>
    </template>
</dom-module>

Podobnie jak w przypadku szablonu, także i tutaj kod został umieszczony w znaczniku dom-module.

W zasadzie nie ma co tutaj komentować poza dwiema rzeczami.
Po pierwsze, został użyty selektor :host, który służy do nadawania stylów najwyższemu elementowi Shadow DOM, czyli Shadow Rootowi. Jak widać, zostało tutaj zastosowane proste rozwiązanie, które polega na tym, że element jest widoczny tylko wtedy, gdy posiada atrybut opened.
Po drugie, zostały użyte zmienne CSS, których mógłbym nie dodawać, lecz wtedy zablokowałbym możliwość stylowania elementów uwięzionych w Shadow DOM. Te zmienne można nadpisać z poziomu odrębnego arkusza stylów.

Przycisk zamykania

Czyżbym o czymś zapomniał? Tak, okno powinno się zamykać po naciśnięciu przycisku. Wróćmy na moment do pliku polymer-modal.js i go zmodyfikujmy.

class PolymerModalElement extends Polymer.Element {
    static get is() {
        return 'polymer-modal';
    }
    static get properties() {
        return {
            opened: {
                type: Boolean,
                value: false,
                reflectToAttribute: true
            },
        };
    }
    ready() {
        super.ready();
        this.shadowRoot.querySelector('.modal__close').addEventListener('click', () => {
            this.opened = false;
        });
    }
}

window.customElements.define(PolymerModalElement.is, PolymerModalElement);

Jak widać, utworzyłem blok metody ready, w którym dodałem listenera do elementu .modal__close. Po naciśnięciu przycisku, do właściwości opened jest przypisywana wartość false, co powoduje zamknięcie się okna.

A jak otworzyć okno? Wystarczy ustawić elementowi atrybut opened.

Podsumowanie

Pokazałem jak zrobić proste okno modalne za pomocą komponentu webowego. To jednak nie wyczerpuje tematu, dlatego w kolejnych wpisach spróbuję pokazać bardziej zaawansowane techniki.