Začínáme s PolymerJS a LitElement

Úvod do webových komponent.

Všichni jsme vytvořili webové stránky pomocí HTML, CSS a JavaScriptu. Obecně platí, že píšeme značky HTML a vykreslujeme je na stránce. Někdy musíme značky opakovat, abychom vykreslili stejný typ prvků uživatelského rozhraní. Dělá to na stránce nepořádek. A také přidání stylů do prvků má vliv na více značek a prvků. Musíme přepsat styl pro každý jiný prvek. Vývojáři se vždy snaží pracovat více za kratší dobu.

Snažíme se dodržovat „Neopakujte se (DRY)“, ale pouze použití HTML, CSS a JavaScriptu není možné. Webové komponenty to umožňují.

Webové komponenty jsou sada rozhraní API webových platforem, která nám umožňují vytvářet nové vlastní HTML značky nebo prvky se zapouzdřenými funkcemi, které lze opakovaně použít a použít na našich webových stránkách. Pomáhá nám sdílet data mezi komponenty a šetří náš čas a energii.

<user-avatar
   class="mr-2x"
   name="${name}"
   shape="${this.shape}"
   .imageURL="${imageURL}"
   .withBorder="${this.withBorder}"
 >
</user-avatar>

Toto je jednoduchý příklad vlastní komponenty. Vlastnosti jako name, shape, imageURL, withBorder se předávají do komponenty ve formě atributů komponenty.

Pokud to vypadá zmateně, nebojte se, na konci tohoto článku budete moci vytvořit jednu webovou aplikaci, kde budeme moci přidávat, upravovat, mazat a vypisovat příspěvky.

Věci, které potřebujete vědět, než se pustíte do výukového programu.

  • Vlastní prvky
    Vlastní prvky pomáhají vývojářům vytvářet přizpůsobitelné prvky nebo značky HTML se zapouzdřenými funkcemi, které jim mohou být užitečné v jejich webových aplikacích. Řekněme, že musíme vytvořit komponentu, která zobrazuje detaily uživatele s obrázky. Můžete vytvořit prvek, kde jej můžete strukturovat, jak chcete.

  • Stínový DOM
    Shadow DOM je způsob, jak zapouzdřit styl a označení vašich komponent. Zabraňuje přepsání stylů. Je to koncept rozsahu stylu. Nenahrazuje styl nadřazených nebo podřízených komponent. Chová se samostatně, což nám umožňuje zapsat styl stejné třídy nebo id do samostatné komponenty.

  • Moduly ES
    Moduly ES definují začlenění a opětovné použití dokumentů JS standardním, modulárním a výkonným způsobem. Webové komponenty se řídí vzorem modulů ES.

  • Šablony HTML
    Šablony HTML jsou způsoby, jak vložit struktury HTML, které se vykreslí pouze při vykreslení hlavní šablony. Cokoli napíšeme do značky, bude vykresleno.

Co je polymer?

Jedná se o open-source JavaScriptovou knihovnu založenou na Web Components. Je vyvinut společností Google. Polymer nám pomáhá vytvářet vlastní prvky pro vytváření webových aplikací. Je mnohem jednodušší a rychlejší vytvářet vlastní prvky, které fungují jako prvky DOM.

Co je LitElement?

Je to jednoduchá základní třída, která nám pomáhá vytvořit webovou komponentu. Používá lit-html k vytváření webových komponent pomocí Shadow DOM a ke správě vlastností a atributů. Prvek se aktualizuje vždy, když se změní vlastnosti prvku.

Toto je základní struktura LitElement pro vytvoření nové komponenty.

import { LitElement, html, css } from 'lit-element';

// Creating MyElement component extending the LitElement Class.
class MyElement extends LitElement {
 // Add Styles for the component
  static get styles() {
    return [
    css `
        :host {
          display:block;
        }
        `];
  }

// Add Properties which will be used into the components.
 static get properties() {
    return {
    myString: { type: String },
    };
  }

// Initialize all the properties and bind the function into the constructor.
  constructor() {
    // Always call super first in constructor
    super();

    this.myString = 'Hello World';
  }

// Add the html structure for the component you want to build.
  render() {
    return html`
    <p>${this.myString}</p>
    `;
  } 
}

// register custom element on the CustomElementRegistry using the define() method
customElements.define('my-element', MyElement);

Nyní se pojďme ponořit do operací CRUD pomocí Polymer a LitElement. Chystáme se vyvinout aplikaci pro přidání, úpravu, odstranění a výpis příspěvku.

GitHub Repo pro tento tutoriál je k dispozici zde. Doporučuji se na to podívat, protože obsahuje celý tento tutoriál.

Dobře, tak pojďme začít!

Stáhněte si startovací soubor zde

Nastavení

Naklonujte úložiště a otevřete jej pomocí textového editoru. Smažte dokumenty , docs-src a testovat . Přejděte na vývoj složku a přesuňte index.html do kořenové složky. Poté můžete smazat dev složku.

Instalovat závislosti:

npm i

Poté nainstalujte @vaadin/router . Je to knihovna routeru na straně klienta vyvinutá v JavaScriptu. Většinou se používá ve webových aplikacích založených na webových komponentách. Je to lehká knihovna routeru. Má různé funkce, jako jsou podřízené trasy, rozlišení asynchronních tras a mnoho dalších.

npm install --save @vaadin/route

Vytvořte src složku. Poté vytvořte komponenty složku uvnitř. Poté vytvořte soubor s názvem post-app.js uvnitř toho. Přidejte níže uvedený kód do post-app.js soubor.

import {LitElement, html} from 'lit';

class PostApp extends LitElement {
   firstUpdated() {
    const el = this.shadowRoot.querySelector('main'); 
  }

   render() {
    return html` <main></main> `;
  }
}
customElements.define('post-app', PostApp);

Zde hlavní je DOM, kde se vykreslují všechny ostatní komponenty.
Vytvořte složku s názvem router uvnitř src složku a také router.js do nově vytvořené složky.

import { Router } from '@vaadin/router';

/**
* Initializes the router.
*
* @param {Object} outlet
*/
function initRouter(outlet) {
const router = new Router(outlet);

 router.setRoutes([
  {
   path: '/',
   component: 'landing-page',
   action: () => {
    import('../components/landing-page/landing-page');
   },
  },
 ]);
}

export default initRouter;

Nyní importujte initRouter do post-app.js

import initRouter from '../router/router';

Zavolejte initRouter funkce uvnitř firstUpdated .

firstUpdated() {
 const el = this.shadowRoot.querySelector('main');
 initRouter(el);
}

Otevřete index.html kořenové složky.

Přidejte značku skriptu do značky head.

<script type="module" src="./src/components/post-app.js"></script>

Přidejte značku komponenty po aplikaci do značky body.

<body>
  <post-app></post-app>
</body>

Budeme používat papírové prvky, které jsou sbírkou vlastních komponent uživatelského rozhraní. Můžeme jej jednoduše nainstalovat a importovat do souboru, který chceme použít, a přidat značku tohoto prvku ve formě HTML Tag. K nastavení kontejneru pozadí pro stránku použijeme prvek papírové karty. Pojďme tedy nainstalovat balíček papírových karet.

npm install @polymer/paper-card --save

Vytvořte vstupní stránku složka Uvnitř složky komponent a také vytvořte landing-page.js do nově vytvořené složky.

import { css, html, LitElement } from 'lit';

import '@polymer/paper-card/paper-card';

class LandingPage extends LitElement {
static get properties() {
 return {};
}

static get styles() {
return [
css`
 .main-wrapper,
  paper-card {
    height: 100vh;
    display: flex;
    flex-direction: column;
   }
`,
];
}

constructor() {
super();
}

render() {
 return html` <div class="main-wrapper">
    <paper-card>
         <div class="menu-wrapper">
          <a href="/home">Home</a>
          <a href="/post">Posts</a>
         </div>
         <div>
          <slot></slot>
         </div>
    </paper-card>
    </div>`;
 }
}

customElements.define('landing-page', LandingPage);

Přidali jsme URL pro stránku Home a Posts, která se vykresluje na všech stránkách, protože jsme přidali /home a /post jako potomky domovského adresáře uvnitř routeru. Nyní je zbývající stránka DOM vykreslena uvnitř slotu. Slot je místo, kam můžeme do komponenty předat cokoli, co chceme vykreslit.

Řekněme, že máme ovocnou komponentu s názvem fruit a chceme obrázek předat do komponenty jako dětský DOM.

fruit_component.js

<div>
  ${this.title}
  <slot></slot>
</div>

Nyní můžeme tímto způsobem předat obrázek jako děti

<fruit_component>
  <img src=”/images/img.jpeg” />
</fruit_component>

Cokoli projdeme mezi komponenty, se zobrazí do slotu.

Otevřeme terminál a spustíme

npm run serve

Zkopírujte místní adresu URL, vložte ji do prohlížeče a otevřete ji.
Zobrazuje seznam nabídek, který jsme přidali do komponenty vstupní stránky.

Teď to nepůjde. Jelikož jsme nenastavili zobrazování jeho obsahu.

  router.setRoutes([
    {
      path: '/',
      component: 'landing-page',
      action: () => {
        import('../components/landing-page/landing-page');
      },
    },
    {
      path: '/',
      component: 'landing-page',
      children: [
        {
          path: '/',
          redirect: '/post',
        },
        {
          path: '/post',
          component: 'post-list',
          action: async () => {
            await import('../components/posts/post-list.js');
          },
        },
        {
          path: '/home',
          component: 'home-page',
          action: () => {
            import('../components/home-page/home-page');
          },
        },
        {
          path: '(.*)+',
          component: 'page-not-found',
          action: () => {
            import('../components/page-not-found');
          },
        },
      ],
    },
  ]);

Nyní vytvořte domovskou stránku složky uvnitř součástí a vytvořte domovskou stránku.js soubor uvnitř něj.

import { LitElement, css, html } from 'lit';

class HomePage extends LitElement {
  static get styles() {
    return [css``];
  }

  render() {
    return html`
      <div>
        Home Page
      </div>
    `;
  }
}
customElements.define('home-page', HomePage);

Vytvořte složku příspěvků uvnitř komponent a vytvořte post-list.js soubor uvnitř něj.

import { css, html, LitElement } from 'lit';

class PostList extends LitElement {
  static get properties() {
    return {};
  }

  static get styles() {
    return [css``];
  }

  constructor() {
    super();
  }

  render() {
    return html`
      <div>
          Post List
      </div>
    `;
  }
}
customElements.define('post-list', PostList);

Nyní obnovujeme stránku a při kliknutí na domovskou stránku vidíme text ‚Domovská stránka‘ a při kliknutí na Příspěvky ‚Seznam příspěvků‘.

Operace načtení

Nyní vytvoříme novou komponentu s názvem ‚table-view‘ pro zobrazení tabulky. Vytvořme složku s názvem common uvnitř src složku. A vytvořte soubor s názvem index.js a table-view.js
Uvnitř index.js importujme soubor table-view.js

import ‘./table-view.js’;

Před otevřením souboru table-view.js , nainstalujme tyto balíčky, které budeme později používat v naší nové komponentě.

npm install --save @polymer/paper-input
npm install --save @polymer/paper-dialog
npm install --save @polymer/paper-button

Otevřete table-view.js a přidejte následující kód.

import { LitElement, html, css } from 'lit';

import '@polymer/paper-input/paper-input';
import '@polymer/paper-dialog/paper-dialog';
import '@polymer/paper-button/paper-button';

export class TableView extends LitElement {
    static get properties() {
        return {
            posts: { type: Array },
        };
    }

    static get styles() {
        return [
            css`
        :host {
        display: block;
        }

        table {
        border: 1px solid black;
        }

        thead td {
        font-weight: 600;
        }

        tbody tr td:last-child {
        display: flex;
        flex-direction: row;
        margin: 0px 12px;
        }

        .mr {
        margin-right: 12px;
        }

        .dflex {
        display: flex;
        flex-direction: column;
        }
        .input-container {
        margin: 4px 4px;
        }

        paper-dialog {
        width: 500px;
        }

        .edit-button {
        background-color: green;
        color: white;
        }

        .delete-button {
        background-color: red;
        color: white;
        }

        .add-button {
        background-color: blue;
        color: white;
        }

        .ml-auto {
        margin-left: auto;
        }
    `,
        ];
    }

    constructor() {
        super();
    }

    renderAddButton() {
        return html`<div class="ml-auto">
    <paper-button raised class="add-button">Add</paper-button>
    </div>`;
    }


    render() {
        return html`
    <div class="dflex">
    ${this.renderAddButton()}
        <div>
        <table>
            <thead>
            <tr>
                <td>S.No.</td>
                <td>Title</td>
                <td>Description</td>
                <td>Action</td>
            </tr>
            </thead>
            <tbody>
            ${this.posts.map((item, index) => {
                return html`
                <tr>
                    <td>${index + 1}</td>
                    <td>${item.title}</td>
                    <td>${item.description}</td>
                    <td>
                    <div class="mr">
                        <paper-button raised class="edit-button">
                        Edit
                        </paper-button>
                    </div>
                    <div>
                      <paper-button raised class="delete-button">
                        Delete
                        </paper-button>
                    </div>
                    </td>
                </tr>
                `;
            })}
            </tbody>
        </table>
        </div>
    </div>
    `;
    }
}
customElements.define('table-view', TableView);

Do post-list.js musíme přidat komponentu table-view abychom po kliknutí na příspěvek viděli tabulku na dané stránce. Musíme předat data příspěvků do komponenty table-view. K tomu musíme vytvořit vlastnost pro ukládání dat příspěvků. Otevřete post-list.js a přidejte novou vlastnost do sekce vlastností.

static get properties() {
    return {
        posts: { type: Array },
    };
}

Po vytvoření vlastnosti ji inicializujme do konstruktoru. Protože jsme nepoužili žádné API, můžeme do něj jednoduše přidat fiktivní data.

constructor() {
    super();

    this.posts = [
        {
            id: 1,
            title: 'Title 1',
            description: 'This is description of post',
        },
        {
            id: 2,
            title: 'Title 2',
            description: 'This is description of post',
        },
        {
            id: 3,
            title: 'Title 3',
            description: 'This is description of post',
        },
    ];
}

Uvnitř funkce renderu zavoláme komponentu table-view a předáme příspěvky jako vlastnost komponenty table-view.

render() {
    return html`
    <div>
        <h2>Post Lists</h2>
        <div>
        <table-view .posts="${this.posts}"></table-view>
        </div>
    </div>
    `;
}

Nyní můžeme vidět naši stránku, jak je zobrazena níže.

Přidat operaci

Nyní pojďme pracovat na přidání položky. Do naší komponenty jsme již přidali tlačítko Přidat.

Nyní aktualizujme renderAddButton přidáním akce kliknutí do něj.

renderAddButton() {
    return html`<div class="ml-auto" @click="${() => this.toggleDialog()}">
    <paper-button raised class="add-button">Add</paper-button>
    </div>`;
  }

Aby bylo tlačítko použitelné, vytvořte toggleDialog funkce pod touto funkcí. Před vytvořením funkce přidejte operaci a vybraná položka vlastnosti do sekce vlastností.

static get properties() {
    return {
    posts: { type: Array },
    operation: { type: String },
    selectedItem: { type: Object },
    };
  }

Po přidání těchto vlastností budeme mít tyto seznamy vlastností. Také musíme inicializovat nově přidané vlastnosti do konstruktoru.

this.operation = 'Add';

this.selectedItem = {};

this.toggleDialog = this.toggleDialog.bind(this);

Nyní můžeme tyto vlastnosti použít v toggleDialog funkce.

toggleDialog(item) {
    if (item) {
        this.operation = 'Edit';
        this.selectedItem = item;
    } else {
        this.operation = 'Add';
    }
}

Přepnout dialog se pokusí otevřít dialog, takže přidejte komponentu dialogu. Použijeme papírový dialog.

openAddEditDialog() {
    return html`<paper-dialog>
    <h2>${this.operation} Post</h2>
    <div class="input-container">
        <paper-input
        label="Title"
        @input="${(event) => this.setItemValue('title', event.target.value)}"
        value="${this.selectedItem.title || ''}"
        ></paper-input>
        <paper-input
        label="Description"
        value="${this.selectedItem.description || ''}"
        @input="${(event) =>
            this.setItemValue('description', event.target.value)}"
        ></paper-input>
    </div>
    <div class="buttons">
        <paper-button dialog-confirm autofocus @click="${this.onAcceptBtnClick}"
        >${this.operation === 'Add' ? 'Save' : 'Update'}</paper-button
        >
        <paper-button dialog-dismiss @click="${this.closeDialog}"
        >Cancel</paper-button
        >
    </div>
    </paper-dialog>`;
}

Součásti papírové karty se musí otevřít po kliknutí na tlačítko Přidat. Chcete-li otevřít dialogové okno, přidejte

this.shadowRoot.querySelector('paper-dialog').open();


na konci toggleDialog Funkce a přidat ${this.openAddEditDialog()} před posledním divem uvnitř funkce render. Tato funkce otevře dialogové okno. A po otevření dialogu musíme dialogové okno zavřít. Za tímto účelem přidejte closeDialog funkce.

  closeDialog() {
    this.shadowRoot.querySelector('paper-dialog').close();
    this.selectedItem = {};
  }

Zde, pokud jsme vybrali libovolnou položku k úpravě, musíme ji vymazat, protože uloží data aktuálně vybrané položky příspěvku.

Zde máme pole Název a Popis pro přidání příspěvků. Vytvořili jsme společné dialogové okno pro přidání a úpravu příspěvků. To nám pomůže nevytvářet stejnou komponentu opakovaně.

Při otevření dialogu musíme nastavit název tlačítka na Uložit při přidávání a aktualizaci při úpravě příspěvku. Proto jsme přidali podmínku na tlačítko přijmout a také název, který se zobrazí při otevření dialogového okna. Přidat příspěvek se zobrazí při Přidat klikněte na tlačítko a Upravit příspěvek se zobrazí při Upravit klikněte na tlačítko.

Nyní musíme získat hodnotu Název a Popis při zadávání do vstupního pole. K tomu musíme přidat novou vlastnost s názvem item do sekce vlastností.

item: { type: Object },

Také jej inicializujte do constructor .

this.item = {};

Nyní vytvořte funkci s názvem setItemValue pod openAddEditDialog funkce.

setItemValue(key, value) {
    this.item = {
        ...this.item,
        [key]: value,
    };
}

Vstup papíru má @input vlastnost, která volá funkci pro přidání položky do proměnné.

@input="${(event) => this.setItemValue('title', event.target.value)}"

Tím předáte klíč a hodnotu do setItemValue funkci a vytvoří objekt.
Přidali jsme @click akce v jednom z tlačítek papíru uvnitř komponenty papír-dialog.

@click="${this.onAcceptBtnClick}"

Kdykoli kliknete na onAcceptBtnClick funkce je volána. Takže musíme vytvořit tuto funkci a také ji svázat uvnitř constructor .

onAcceptBtnClick() {
    if (this.operation === 'Add') {
        this.item = {
            id: this.posts.length + 1,
            ...this.item
        };
        this.posts = [...this.posts, this.item];
    }
}

this.onAcceptBtnClick = this.onAcceptBtnClick.bind(this);

Když je hodnota operace ‚Přidat‘, bude nová položka přidána do příspěvků.

Nyní je funkce Přidat dokončena. Do příspěvku můžeme přidat nová data.

Upravit operaci

Je čas upravit příspěvek.

Pro úpravu příspěvku musíme přidat @click akci do tlačítka Upravit. Pojďme tedy aktualizovat tlačítko Upravit uvnitř tabulky.

<div class="mr" @click="${() => this.toggleDialog(item)}">
 <paper-button raised class="edit-button">
   Edit
 </paper-button>
</div>

musíme aktualizovat setItemValue funkce. Nastavili jsme vybrané položky, které jsme se rozhodli upravit na selectedItem vlastnost na toggleDialog funkce. Nyní můžeme aktualizovat setItemValue funkce. Když operation je nastaven na Upravit, aktualizuje se na this.selectedItem vlastnost, když aktualizujeme hodnotu.

setItemValue(key, value) {
    if (this.operation === 'Edit') {
        this.selectedItem = {
            ...this.selectedItem,
            [key]: value,
        };
    } else {
        this.item = {
            ...this.item,
            [key]: value,
        };
    }
}

Nyní musíme aktualizovat onAcceptBtnClick funkce, kde je aktualizovaný příspěvek nahrazen novým.

onAcceptBtnClick() {
    if (this.operation === 'Add') {
        this.item = {
            id: this.posts.length + 1,
            ...this.item
        };
        this.posts = [...this.posts, this.item];
    } else {
        this.posts = this.posts.map((post) => {
            if (post.id === this.selectedItem.id) {
                return this.selectedItem;
            }
            return post;
        });
    }
}

Toto skončí ve funkci Upravit příspěvku.

Operace odstranění

Nyní přejdeme k funkci Smazat příspěvek.

Nejprve musíme přidat @click akci do tlačítka smazat.

<div @click="${() => this.handleOnDelete(item)}">
 <paper-button raised class="delete-button">
   Delete
 </paper-button>
</div>

Nyní musíme vytvořit handleOnDelete funkci a svázat ji do konstruktoru.

 handleOnDelete(item) {
    this.posts = this.posts.filter((post) => {
    return post.id !== item.id;
    });
  }
this.handleOnDelete = this.handleOnDelete.bind(this);

Zde se položka příspěvku, kterou chceme smazat, předá funkci a porovnáme její ID s příspěvkem v poli Příspěvky. Poté je příspěvek smazán z pole příspěvků.

Tímto způsobem můžeme provést jednoduchou operaci CRUD pomocí PolymerJS a LitElement.


No