Aan de slag met PolymerJS en LitElement

Inleiding tot webcomponenten.

We hebben allemaal webpagina's gebouwd met HTML, CSS en JavaScript. Over het algemeen schrijven we de HTML-tags en geven ze weer op de pagina. Soms moeten we tags herhalen om hetzelfde type UI-elementen weer te geven. Het maakt de pagina rommelig. En ook het toevoegen van stijlen aan de elementen heeft invloed op meerdere tags en elementen. We moeten de stijl voor elk ander element overschrijven. Ontwikkelaars proberen altijd meer te werken in minder tijd.

We proberen "Do not Repeat Yourself (DRY)" te volgen, maar alleen HTML, CSS en JavaScript gebruiken is niet mogelijk. Webcomponenten maken het mogelijk.

Webcomponenten zijn een reeks webplatform-API's waarmee we nieuwe aangepaste HTML-tags of -elementen met ingekapselde functionaliteit kunnen maken die meerdere keren kunnen worden hergebruikt en op onze webpagina's kunnen worden gebruikt. Het helpt ons gegevens tussen componenten te delen en bespaart ons tijd en energie.

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

Dit is het eenvoudige voorbeeld van een gebruikerscomponent. Eigenschappen zoals naam, vorm, imageURL, withBorder worden in de component doorgegeven in de vorm van componentattributen.

Als dit er verwarrend uitziet, maak je geen zorgen, aan het einde van dit artikel kun je één webtoepassing bouwen waar we de berichten kunnen toevoegen, bewerken, verwijderen en weergeven.

Dingen die je moet weten voordat je de tutorial induikt.

  • Aangepaste elementen
    Aangepaste elementen helpen ontwikkelaars bij het bouwen van hun aanpasbare element of HTML-tags met ingekapselde functionaliteit die voor hen nuttig kan zijn in hun webapplicaties. Laten we zeggen dat we een component moeten maken die gebruikersdetails weergeeft met afbeeldingen. Je kunt een element maken waar je het kunt structureren zoals je wilt.

  • Schaduw DOM
    Shadow DOM is een manier om de styling en opmaak van uw componenten in te kapselen. Het voorkomt het overschrijven van stijlen. Het is het concept van de scoped-stijl. Het vervangt niet de stijl van bovenliggende of onderliggende componenten. Het gedraagt ​​zich afzonderlijk waardoor we de stijl van dezelfde klasse of id in een aparte component kunnen schrijven.

  • ES-modules
    ES Modules definieert de opname en hergebruik van JS-documenten op een standaardgebaseerde, modulaire, performante manier. Webcomponenten volgen het patroon van ES Modules.

  • HTML-sjablonen
    HTML-sjablonen zijn manieren om HTML-structuren in te voegen die alleen worden weergegeven wanneer de hoofdsjabloon wordt weergegeven. Alles wat we in de tag schrijven, wordt weergegeven.

Wat is polymeer?

Het is een open-source JavaScript-bibliotheek op basis van webcomponenten. Het is ontwikkeld door Google. Polymer helpt ons om op maat gemaakte elementen te maken voor het bouwen van webapplicaties. Het is veel gemakkelijker en sneller om aangepaste elementen te maken die werken als DOM-elementen.

Wat is LitElement?

Het is een eenvoudige basisklasse die ons helpt bij het maken van een webcomponent. Het gebruikt lit-html om de webcomponenten te maken met behulp van Shadow DOM en de eigenschappen en attributen te beheren. Het element wordt bijgewerkt wanneer de eigenschappen van het element worden gewijzigd.

Dit is de basisstructuur van LitElement om een ​​nieuwe component te maken.

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);

Laten we nu eens duiken in CRUD-bewerkingen met behulp van Polymer en LitElement. We gaan een applicatie ontwikkelen om de post toe te voegen, te bewerken, te verwijderen en weer te geven.

De GitHub Repo voor deze zelfstudie is hier beschikbaar. Ik raad aan om het te bekijken, omdat het deze hele tutorial heeft.

Oké, laten we beginnen!

Download hier het startersbestand

Instellen

Kloon de opslagplaats en open deze met de teksteditor. Verwijder de documenten , docs-src , en test . Ga naar de dev map en verplaats de index.html in de hoofdmap. Daarna kunt u de dev . verwijderen map ook.

Afhankelijkheden installeren:

npm i

Installeer daarna @vaadin/router . Het is een client-side routerbibliotheek ontwikkeld in JavaScript. Het wordt meestal gebruikt in op Web Components gebaseerde webapplicaties. Het is een lichtgewicht routerbibliotheek. Het heeft verschillende functies, zoals onderliggende routes, resolutie van asynchrone routes en nog veel meer.

npm install --save @vaadin/route

Maak een src map. Maak daarna componenten map erin. Maak vervolgens een bestand met de naam post-app.js in het. Voeg de onderstaande code toe aan de post-app.js het dossier.

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);

Hier is main de DOM waar alle andere componenten worden weergegeven.
Maak een map met de naam router binnen de src map en ook router.js in de nieuw aangemaakte map.

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;

Importeer nu de initRouter in de post-app.js

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

Bel de initRouter functie binnen de firstUpdated .

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

Open index.html van de hoofdmap.

Voeg de scripttag toe aan de head-tag.

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

Voeg de post-app-componenttag toe aan de body-tag.

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

We zullen de papieren elementen gebruiken die een verzameling aangepaste UI-componenten zijn. We kunnen het eenvoudig installeren en importeren in het bestand dat we willen gebruiken en de tag van dat element toevoegen in de vorm van een HTML-tag. We gaan een papieren kaartelement gebruiken om de achtergrondcontainer voor de pagina in te stellen. Laten we dus het papieren kaartpakket installeren.

npm install @polymer/paper-card --save

Maak de bestemmingspagina map Binnen de componentenmap en maak ook landing-page.js in de nieuw aangemaakte map.

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);

We hebben de URL toegevoegd voor de Home- en Posts-pagina die in alle pagina's wordt weergegeven, omdat we /home en /post hebben toegevoegd als onderliggende items van een homedirectory in de router. Nu wordt de resterende pagina-DOM in de sleuf weergegeven. Een slot is een plaats waar we alles wat we willen weergeven in de component kunnen doorgeven.

Laten we zeggen dat we een fruitcomponent hebben met de titel fruit en dat we de afbeelding in de component willen doorgeven als een DOM voor kinderen.

fruit_component.js

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

Nu kunnen we het beeld als kinderen op deze manier doorgeven

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

Wat we ook tussen de componenten doorgeven, wordt in de sleuf weergegeven.

Laten we de terminal openen en uitvoeren

npm run serve

Kopieer de lokale URL en plak deze in de browser en open deze.
Het toont de menulijst die we hebben toegevoegd aan de bestemmingspagina-component.

Het zal nu niet lukken. Omdat we niet hebben ingesteld om de inhoud ervan weer te geven.

  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');
          },
        },
      ],
    },
  ]);

Maak nu een home-page map binnen de componenten map en maak de home-page.js bestand erin.

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);

Maak een posts-map binnen de componenten map en maak de post-list.js bestand erin.

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);

Nu de pagina wordt vernieuwd, kunnen we de tekst 'Home page' zien terwijl we op Home klikken en 'Post List' terwijl we op Posts klikken.

Ophaalbewerking

Laten we nu een nieuwe component maken met de naam 'table-view' om de tabel weer te geven. Laten we een map maken met de naam algemeen binnen de src map. En maak een bestand met de naam index.js en table-view.js
Binnen index.js laten we de table-view.js . importeren

import ‘./table-view.js’;

Voordat u de table-view.js . opent , laten we deze pakketten installeren die we later in onze nieuwe component zullen gebruiken.

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

Open table-view.js en voeg de volgende code toe.

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);

We moeten een tabelweergavecomponent toevoegen aan post-list.js zodat wanneer we op een bericht klikken, we de tabel op die pagina kunnen zien. We moeten de berichtgegevens doorgeven aan de component tabelweergave. Daarvoor moeten we een eigenschap maken om de gegevens van berichten op te slaan. Open post-list.js en voeg nieuwe eigenschap toe aan de eigenschap sectie.

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

Nadat we de eigenschap hebben gemaakt, initialiseren we deze in een constructor. Omdat we geen API hebben gebruikt, kunnen we er eenvoudig dummy-gegevens aan toevoegen.

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',
        },
    ];
}

Laten we in de renderfunctie de component tabelweergave aanroepen en de berichten doorgeven als een eigenschap van de component tabelweergave.

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

Nu kunnen we onze pagina zien zoals hieronder weergegeven.

Bewerking toevoegen

Laten we nu werken aan het toevoegen van een item. We hebben al een knop Toevoegen aan onze component toegevoegd.

Laten we nu de renderAddButton . bijwerken functie door de klikactie eraan toe te voegen.

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

Om de knop bruikbaar te maken, maken we een toggleDialog functie onder deze functie. Laten we, voordat we de functie maken, bewerking toevoegen en selectedItem eigenschappen in het eigenschappengedeelte.

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

We hebben deze lijsten met eigenschappen nadat we die eigenschappen hebben toegevoegd. We moeten ook de nieuw toegevoegde eigenschappen initialiseren in de constructor.

this.operation = 'Add';

this.selectedItem = {};

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

Nu kunnen we deze eigenschappen gebruiken in de toggleDialog functie.

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

Het dialoogvenster Toggle zal proberen het dialoogvenster te openen, dus laten we een dialoogcomponent toevoegen. We zullen papier-dialoog gebruiken.

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>`;
}

Papieren kaartcomponenten moeten worden geopend wanneer op de knop Toevoegen wordt geklikt. Om het dialoogvenster te openen laten we toevoegen

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


aan het einde van de toggleDialog Functie en voeg ${this.openAddEditDialog()} toe vóór de laatste div binnen de renderfunctie. Deze functie opent het dialoogvenster. En na het openen van het dialoogvenster moeten we het dialoogvenster sluiten. Laten we hiervoor de closeDialog . toevoegen functie.

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

Als we hier een item hebben geselecteerd om te bewerken, moeten we het wissen omdat het de gegevens van het momenteel geselecteerde postitem zal opslaan.

Hier hebben we een veld Titel en Beschrijving om berichten toe te voegen. We hebben een gemeenschappelijk dialoogvenster gemaakt voor het toevoegen en bewerken van berichten. Dit zal ons helpen om niet herhaaldelijk dezelfde component te maken.

Bij het openen van het dialoogvenster moeten we de knopnaam instellen als Opslaan tijdens het toevoegen en Bijwerken tijdens het bewerken van het bericht. Daarom hebben we de voorwaarde toegevoegd aan de acceptatieknop en ook de titel die moet worden weergegeven wanneer het dialoogvenster wordt geopend. Bericht toevoegen wordt weergegeven wanneer de Toevoegen knop is geklikt en Bericht bewerken wordt weergegeven wanneer de Bewerken knop is geklikt.

Nu moeten we de waarde van Titel en Beschrijving krijgen wanneer ze in het invoerveld worden getypt. Om dit te doen, moeten we de nieuwe eigenschap met de naam item toevoegen in het eigenschappengedeelte.

item: { type: Object },

Initialiseer het ook in de constructor .

this.item = {};

Maak nu een functie met de naam setItemValue onder de openAddEditDialog functie.

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

Papierinvoer heeft de @input eigenschap die een functie aanroept om het item aan de variabele toe te voegen.

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

Dit geeft de sleutel en de waarde door aan de setItemValue functie en zal het object maken.
We hebben de @click . toegevoegd actie in een van de papieren knoppen in de papierdialoogcomponent.

@click="${this.onAcceptBtnClick}"

Telkens wanneer op de onAcceptBtnClick . wordt geklikt functie wordt genoemd. Dus we moeten die functie maken en deze ook binden in de 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);

Wanneer de bewerkingswaarde 'Toevoegen' is, wordt het nieuwe item toegevoegd aan berichten.

Nu is de functie Toevoegen voltooid. We kunnen nieuwe gegevens aan het bericht toevoegen.

Bewerking bewerken

Het is tijd om het bericht te bewerken.

Om het bericht te bewerken, moeten we de @click . toevoegen actie in de edit-knop. Laten we dus de bewerkingsknop in de tabel bijwerken.

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

we moeten de setItemValue update updaten functie. We hebben geselecteerde items die we hebben gekozen om te bewerken ingesteld op de selectedItem eigendom op de toggleDialog functie. We kunnen nu de setItemValue . updaten functie. Wanneer de operation is ingesteld op Bewerken, wordt bijgewerkt op this.selectedItem eigenschap wanneer we de waarde bijwerken.

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

Nu moeten we de onAcceptBtnClick . updaten functie waarbij de bijgewerkte post wordt vervangen door de nieuwe.

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;
        });
    }
}

Dit komt terecht in de functie Bewerken van het bericht.

Bewerking verwijderen

Laten we nu verder gaan met de functie Verwijderen van het bericht.

Ten eerste moeten we @click . toevoegen actie in de delete-knop.

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

Nu moeten we de handleOnDelete . maken functie en bind het in de constructor.

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

Hier wordt het berichtitem dat we willen verwijderen doorgegeven aan de functie en we vergelijken de ID ervan met het bericht in de array Posts. Daarna wordt het bericht verwijderd uit de berichtenreeks.

Op deze manier kunnen we een eenvoudige CRUD-bewerking uitvoeren met PolymerJS en LitElement.


No