Teil 2 - Basis-Layout implementieren
Dieses Lab ist Teil der Amenity Finder Serie, einer umfassenden Anleitung, die Schritt für Schritt zeigt, wie man eine Web-Applikation mithilfe von Web-Components entwickelt. In jedem Teil der Serie werden spezifische Aspekte der Entwicklung behandelt und es werden bewährte Methoden und Techniken vermittelt, um eine moderne und skalierbare Webanwendung aufzubauen.
Im zweiten Teil der Serie konzentrieren wir uns auf die Implementierung des Basis-Layouts unserer Amenity Finder Applikation. Wir erstellen die grundlegende Struktur und das Design, um eine solide Grundlage für die weiteren Funktionen zu schaffen.
Vorschau

Wenn du dieses Lab bereits abgeschlossen hast, geht es im dritten Teil der Amenity Finder Serie weiter. Dort lernst du, wie du die Amenity Finder Applikation in einzelne Seiten unterteilen und das Routing implementieren kannst.
Vorbereitung
Ziele
Dieses Lab konzentriert sich auf die Erreichung der folgenden Ziele:
- Überprüfung und Optimierung der Verzeichnisstruktur: Wir gehen die Verzeichnisstruktur des Projekts durch und optimieren sie, um eine bessere Organisation und Strukturierung unserer Amenity Finder Applikation zu ermöglichen. Dies umfasst die Aufteilung der Dateien und Ordner in sinnvolle Kategorien und das Entfernen unnötiger oder überflüssiger Elemente.
 - Implementierung des grundlegenden Applikationslayouts mit Material Design Web-Components (MWC): Wir nutzen die Material Design Web-Components (MWC), um das grundlegende Layout unserer Amenity Finder Applikation zu gestalten. MWC bietet eine Sammlung von vordefinierten, reaktionsfähigen und ansprechenden Web-Components, die auf den Designprinzipien des Material Designs basieren. Durch die Implementierung des Layouts mit MWC können wir eine konsistente und moderne Benutzeroberfläche schaffen.
 - Implementierung einer Seitenleiste mit Auf- und Zuklapp-Mechanismus: Eine Seitenleiste ist ein nützliches Element, um zusätzliche Navigationsoptionen oder Informationen in unserer Applikation anzuzeigen. Wir werden eine Seitenleiste implementieren, die die Möglichkeit bietet, sie auf- und zuzuklappen. Dadurch können Benutzer den verfügbaren Bildschirmplatz optimal nutzen und bei Bedarf die Seitenleiste ein- oder ausblenden.
 
Schritte
Das Basis Layout für die Applikation wird in 6 Schritten erstellt:
- Verzeichnisstruktur durchgehen
 - Verzeichnisstruktur anpassen
 - Layout mit Material Design
 - Layout Anpassungen und Theming
 - Navigation in der Sidebar einbauen
 - Sidebar ein- und ausblenden
 
Branch
Der zweite Teil des Amenity Finder Labs ist auf GitHub verfügbar.
Wichtige Links
Für weitere Informationen zu den behandelten Themen findest du nützliche Ressourcen unter den folgenden Links:
Los geht’s!
Verzeichnisstruktur durchgehen
In der vorherigen Lektion haben wir mittels des open-wc Generators unser Projekt aufgesetzt und die folgende Verzeichnisstruktur erhalten:
amenity-finder/
├── assets/
│   └── open-wc-logo.svg
├── src/
│   ├── amenity-finder.js
│   └── AmenityFinder.js
├── .editorconfig
├── .gitignore
├── custom-elements.json
├── index.html
├── LICENSE
├── package.json
├── README.md
├── rollup.config.js
└── web-dev-server.config.mjsWir gehen jetzt auf die Struktur und einige ausgewählte Dateien ein.
index.htmlDas ist der Einstiegspunkt in unsere Applikation. Hier definieren wir das Basis-HTML und binden unsere Applikation ein:
<amenity-finder></amenity-finder> <script type="module" src="./src/amenity-finder.js"></script>src/AmenityFinder.jsHier ist unsere Einstiegskomponente als JavaScript-Klasse und Erweiterung von
Litdefiniert. Sie ist das oberste Element in unserem Komponenten-Baum und fungiert als «Orchestrator» unserer Applikation. In Angular ist das Pendant dazu dasAppModule, in der VueJS-Welt wäre es z. B. dieApp.vueund bei React (erstellt mit Create React App) dasApp.js.src/amenity-finder.jsDieses File ist ein simpler Wrapper rund um unsere
AmenityFinderKlasse. Es verbindet die Funktionalität unserer Applikation mit dem HTML (DOM) mittels Custom Elements API:customElements.define('amenity-finder', AmenityFinder);Hier teilen wir dem Browser mit: jedes Mal, wenn du den Tag
<amenity-finder>siehst, kannst du die KlasseAmenityFinderinstanziieren.Die Trennung der Klasse vom Custom-Tag macht beim Publizieren von Komponenten durchaus Sinn. So kann nämlich die Klasse
AmenityFinderimportiert und verwendet werden, ohne dass dabei auch das Custom Element definiert wird.custom-elements.jsoncustom-elements-json ist ein Dateiformat zur Beschreibung von Custom Elements. Es soll die Struktur einer Webkomponente einfacher maschinenlesbar machen und so zu besserem Editor-Support, Dokumentation oder Linting verhelfen. Die Spezifikation ist im Juli 2021 in der Version 1 released worden.
rollup.config.jsFür die Entwicklung verwenden wir den @web/dev-server. Der Server kann «nackte Imports» (Imports ohne Angabe eines Pfads) on-the-fly umschreiben, sodass der Browser damit umgehen kann. Zum Beispiel wird im
AmenityFinder.jsdie Zeile:import { LitElement, html, css } from 'lit';zu
import { LitElement, html, css } from '../node_modules/lit-element/index.js';Weil der Server standardmässig von unserem Repository-Root aus alle Dateien ausliefert, kann der Browser diese umgeschriebenen Pfade direkt selber auflösen.

Für den Produktions-Build verwendet open-wc rollup.js. Beim Build werden, ausgehend vom
index.html, alle Dependencies aufgelöst und landen schlussendlich in nur einem JavaScript-File (auch als «Bundle» bezeichnet), welches wiederum in einem transformiertenindex.htmlreferenziert wird.
package.json
Der Vollständigkeit halber gehen wir noch auf die wichtigsten npm-Skripte im package.json ein:
startStartet den @web/dev-server Server mit
index.htmlals Applikation-Index-File. Der server wird mit der Dateiweb-dev-server.config.mjskonfiguriert. Hier ist unter anderem dasappIndexproperty relevant. Damit wird das Auto-Rewrite (von Requests) auf eine Index-Datei (z.B.index.html) aktiviert, sodass ein Single-Page-Applikation (SPA) Routing (im Client) möglich ist.WICHTIG: Während der Entwicklung arbeiten wir lokal mit diesem Befehl (
$ npm start). Beim Installieren neuer Abhängigkeiten ($ npm install) muss der Befehl neu gestartet werden.buildErstellt einen Produktions-Build der Applikation mithilfe von Rollup. Die Konfiguration dazu ist im
rollup.config.jsdefiniert. Standardmässig landet der Build unserer Applikation imdistVerzeichnis.start:buildErstellt zuerst einen Produktions-Build der Applikation mit
buildund stellt diesen danach unter einerlocalhostURL über @web/dev-server zur Verfügung. Damit kann der Produktions-Build lokal getestet werden.lintÜberprüft den Code auf Lint-Fehler mithilfe von ESLint und Prettier.
formatFormatiert den Code mithilfe von Prettier und korrigiert alle ESLint-Fehler, welche automatisch behoben werden können.
Verzeichnisstruktur anpassen
Wir können nun die Verzeichnisstruktur etwas entschlacken und vereinfachen.
AmenityFinder.jsundamenity-finder.jskombinierenWir können die Zeile
customElements.define('amenity-finder', AmenityFinder);ausamenity-finder.jsentfernen und am Schluss der DateiAmenityFinder.jseinfügen.amenity-finder.jswerden wir später löschen.--- src/AmenityFinder.js +++ src/AmenityFinder.js `; } } + +customElements.define('amenity-finder', AmenityFinder);index.htmlanpassenHier müssen wir nun die
AmenityFinder.jsstattamenity-finder.jsreferenzieren--- index.html +++ index.html <body> <amenity-finder></amenity-finder> - <script type="module" src="./src/amenity-finder.js"></script> + <script type="module" src="./src/AmenityFinder.js"></script> </body>Nicht benötigte Dateien löschen
Mit
$ rm src/amenity-finder.js assets/open-wc-logo.svglöschen wir Dateien, die wir nicht mehr benötigen. Die Referenz zum open-wc-Logo müssen wir imAmenityFinder.jsnoch entfernen:--- src/AmenityFinder.js +++ src/AmenityFinder.js @@ -1,7 +1,5 @@ import { LitElement, html, css } from 'lit'; -const logo = new URL('../assets/open-wc-logo.svg', import.meta.url).href; - export class AmenityFinder extends LitElement { static get properties() { return { @@ -62,7 +60,6 @@ render() { return html` <main> - <div class="logo"><img alt="open-wc logo" src=${logo} /></div> <h1>${this.title}</h1> <p>Edit <code>src/AmenityFinder.js</code> and save to reload.</p>Custom Elements Manifest + Analyzer entfernen (optional)
Weil wir keine Webkomponente als Library publizieren, sondern eine Applikation bauen wollen, können wir zusätzlich auch noch auf das Custom Elements Manifest und die damit verbundenen Dateien und abhängigkeiten verzichten. Der Build-Prozess wird dadurch etwas einfacher und schneller.
Wir können die Datei
custom-elements.jsonlöschen sowie die dazugehörigen Skripte und Dependencies aus dempackage.jsonentfernen:--- package.json +++ package.json @@ -7,9 +7,8 @@ "scripts": { "lint": "eslint --ext .js,.html . --ignore-path .gitignore && prettier \"**/*.js\" --check --ignore-path .gitignore", "format": "eslint --ext .js,.html . --fix --ignore-path .gitignore && prettier \"**/*.js\" --write --ignore-path .gitignore", - "build": "rimraf dist && rollup -c rollup.config.js && npm run analyze -- --exclude dist", + "build": "rimraf dist && rollup -c rollup.config.js", "start:build": "web-dev-server --root-dir dist --app-index index.html --open", - "analyze": "cem analyze --Lit", "start": "web-dev-server", "deploy": "npm run build && surge --domain itchy-frog.surge.sh dist" }, @@ -18,7 +17,6 @@ }, "devDependencies": { "@babel/preset-env": "^7.15.4", - "@custom-elements-manifest/analyzer": "^0.4.17", "@open-wc/building-rollup": "^1.10.0", "@open-wc/eslint-config": "^4.3.0", "@rollup/plugin-babel": "^5.3.0",Danach führen wir noch
$ npm installaus, damit unserpackage-lock.jsonFile auf die neuen Vorgaben aus dempackage.jsonangepasst wird.
Die neue Verzeichnisstruktur sollte nun wie folgt aussehen:
amenity-finder/
├── assets/
├── src/
│   └── AmenityFinder.js
├── .editorconfig
├── .gitignore
├── index.html
├── LICENSE
├── package.json
├── package-lock.json
├── README.md
├── rollup.config.js
└── web-dev-server.config.mjsDie App sieht im Browser nun wie folgt aus:

Layout mit Material Design
Damit wir uns auf die eigentliche Funktionalität konzentrieren können, verwenden wir für das Layout unserer Applikation Material Design. Material Design ist das Design System von Google mit Implementationen von Android, iOS, Flutter und für das Web.
Die Web-Version der Material Design Components ist in Plain-JavaScript1 implementiert. Neben diversen Third-Party Framework Wrappern gibt es auch eine offizielle Implementation mit Web-Components (material-components/material-components-web-components).
Basis-Layout Definition
Unser Applikation-Layout soll grundsätzlich aus drei Teilen bestehen:
- eine globale Titel-Leiste, die jeweils anzeigt, in welchem Bereich der Applikation wir uns befinden
 - eine Seitenleiste (Sidebar), in der wir unsere Navigation unterbringen können
 - einem Bereich für den eigentlichen Inhalt jeder Seite unserer Applikation
 
Für das Basis-Layout können wir die folgenden zwei MWC verwenden
<mwc-drawer>für das generelle Layout mit Sidebar und Hauptinhalt- Diese Komponente hat einen 
default«Slot», diesen verwenden wir für die Sidebar. - Weiter hat die Komponente einen 
appContentSlot. Dieser wird unseren Seiteninhalt beherbergen. 
- Diese Komponente hat einen 
 <mwc-top-app-bar>für den Titelbereich beim Hauptinhalt- Hat einen named Slot 
titlefür den Titel 
- Hat einen named Slot 
 
Grafisch sieht die Zusammensetzung der Komponenten wie folgt aus:
MWC einbinden
Wir installieren als Erstes zwei neue Web-Components wie folgt (ACHTUNG: Workaround):
Wir entfernen zuerst das dependencies Keyword aus package.json
--- package.json
+++ package.json
@@ -12,9 +12,6 @@
     "start": "web-dev-server",
     "deploy": "npm run build && surge --domain itchy-frog.surge.sh dist"
   },
-  "dependencies": {
-    "lit": "^2.0.0-rc.4"
-  },
   "devDependencies": {
     "@babel/preset-env": "^7.15.4",
     "@open-wc/building-rollup": "^1.10.0",
und entfernen das package-lock.json sowie das gesamte node_modules Verzeichnis
$ rm -rf package-lock.json node_modulesund führen danach $ npm install aus. Anschliessend führen wir die folgenden Befehle im Terminal aus:
$ npm install lit-html@^1.4.1
$ npm install lit@^2.0.0-rc.4
$ npm install @material/mwc-drawer@^0.22.1 @material/mwc-top-app-bar@^0.22.1Technischer Hintergrund für diesen Workaround
Eigentlich wäre die Installation einfacher, nämlich
$ npm install @material/mwc-drawer @material/mwc-top-app-bar. Das @open-wc-Template arbeitet allerdings mit einer neueren Version von Lit (2.x) als derjenigen, die bei MWC verwendet wird (1.x). Wegen der Art und Weise, wie NPM mit verschachtelten Abhängigkeiten umgeht und einer ungünstigen Implementation eines Features in lit 1.x, müssen wir zuerst die Version 1.x installieren und erst danach alle Abhängigkeiten mit 2.x.Wir können jetzt diese beiden Web-Components im AmenityFinder.js importieren und einbinden:
import '@material/mwc-drawer';
import '@material/mwc-top-app-bar';Die render-Methode im AmenityFinder.js können wir mit dem folgenden Inhalt überschreiben:
<mwc-drawer>
  <div>
    <p>Drawer Content!</p>
  </div>
  <div slot="appContent">
    <mwc-top-app-bar>
      <div slot="title">Title</div>
    </mwc-top-app-bar>
    <div>
      <p>Main Content!</p>
    </div>
  </div>
</mwc-drawer>und entfernen mal alles, was wir in dieser Klasse nicht benötigen. Die AmenityFinder-Klasse sieht danach wie folgt aus:
src/AmenityFinder.js
import { LitElement, html, css } from 'lit';
import '@material/mwc-drawer';
import '@material/mwc-top-app-bar';
export class AmenityFinder extends LitElement {
  static get styles() {
    return css`
      :host {
        min-height: 100vh;
      }
    `;
  }
  render() {
    return html`
      <mwc-drawer>
        <div>
          <p>Drawer Content!</p>
        </div>
        <div slot="appContent">
          <mwc-top-app-bar>
            <div slot="title">Title</div>
          </mwc-top-app-bar>
          <div>
            <p>Main Content!</p>
          </div>
        </div>
      </mwc-drawer>
    `;
  }
}
customElements.define('amenity-finder', AmenityFinder);Unsere Applikation sollte nun in etwa wie folgt aussehen:

Einen Schönheitspreis gewinnen wir damit (noch) nicht, es geht allerdings schon in die richtige Richtung und die Einbindung der beiden Komponenten hat funktioniert.
Layout Anpassungen und Theming
Die MWC sind eingebunden und das grundsätzliche Layout funktioniert. In diesem Kapitel machen wir nun aber noch den Feinschliff und passen das Layout und Inhalte so an, dass wir eine gute Basis für die Weiterentwicklung unserer Applikation haben.
Der <mwc-drawer> wird noch nicht über die gesamte Höhe des Browserfensters gestreckt. In den Entwicklertools können wir die Situation untersuchen und stellen fest, dass das <html>-Element eine noch zu geringe Höhe hat:

Dies können wir mit einem Einzeiler im index.html korrigieren:
--- index.html
+++ index.html
       margin: 0;
       padding: 0;
       font-family: sans-serif;
       background-color: #ededed;
+      height: 100%;
     }
   </style>
   <title>amenity-finder</title>
Damit wir noch den Vorgaben bezüglich Schriftart und Icons von Material Design entsprechen, müssen wir die Roboto und Material Icons Schriftarten wie folgt einbinden:
--- index.html
+++ index.html
   <meta name="Description" content="Put your description here.">
   <base href="/">
+  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
+  <link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet">
+
   <style>
     html,
     body {
Nun werden wir noch die Default Material Design Farbe los. Material Design kommt mir einem Theming System daher. Damit können viele Aspekte der MWC angepasst und überschrieben werden. MWC unterstützt das Theming mittels CSS Custom Properties.
Die Details dazu sind im MWC Theming Guide beschrieben. In einem ersten Schritt passen wir die primäre Farbe an, damit wir das (subjektiv) nicht so schöne Violett los sind und nehmen dafür das Inventage Blau. Ihr könnt selbstverständlich eine eigene Farbe wählen.
--- index.html
+++ index.html
       font-family: sans-serif;
       background-color: #ededed;
       height: 100%;
+
+      --mdc-theme-primary: #004996;
     }
   </style>
   <title>amenity-finder</title>
Unsere Applikation sollte nun in etwa wie folgt aussehen:

Navigation in der Sidebar einbauen
In diesem Schritt bereiten wir die Navigation unserer Applikation vor. Diese werden wir in die folgenden Bereiche einteilen:
- Home (Startseite)
 - Search (Seite für die Suche)
 - Results (Seite für Suchresultate)
 
<mwc-list> für Navigationselemente
Für die Navigationspunkte können wir uns der <mwc-list> Komponente bedienen. Zuerst installieren wir diese mit:
$ npm install @material/mwc-list@^0.22.1Importieren diese entsprechend der Dokumentation im AmenityFinder.js:
import '@material/mwc-list/mwc-list.js';
import '@material/mwc-list/mwc-list-item.js';und binden den dazugehören HTML-Code als Inhalt vom <mwc-drawer> ein:
<mwc-list>
  <mwc-list-item>Home</mwc-list-item>
  <mwc-list-item>Search</mwc-list-item>
  <mwc-list-item>Results</mwc-list-item>
</mwc-list>Unsere AmenityFinder-Klasse sieht nun so aus:
src/AmenityFinder.js
import { LitElement, html, css } from 'lit';
import '@material/mwc-drawer';
import '@material/mwc-top-app-bar';
import '@material/mwc-list/mwc-list.js';
import '@material/mwc-list/mwc-list-item.js';
export class AmenityFinder extends LitElement {
  static get styles() {
    return css`
      :host {
        min-height: 100vh;
      }
    `;
  }
  render() {
    return html`<mwc-drawer>
      <mwc-list>
        <mwc-list-item>Home</mwc-list-item>
        <mwc-list-item>Search</mwc-list-item>
        <mwc-list-item>Results</mwc-list-item>
      </mwc-list>
      <div slot="appContent">
        <mwc-top-app-bar> <div slot="title">Title</div> </mwc-top-app-bar>
        <div><p>Main Content!</p></div>
      </div>
    </mwc-drawer>`;
  }
}
customElements.define('amenity-finder', AmenityFinder);Die <mwc-drawer> Komponente kennt noch einen zusätzlichen Slot title. Mit dessen Hilfe können wir Inhalt, der als Titel in der Sidebar dargestellt wird, definieren. Damit dieser dargestellt wird, muss beim <mwc-drawer> noch das Attribut hasHeader gesetzt sein:
--- src/AmenityFinder.js
+++ src/AmenityFinder.js
   render() {
     return html`
-      <mwc-drawer>
-        <div>
-          <p>Drawer Content!</p>
-        </div>
+      <mwc-drawer hasHeader>
+        <span slot="title">Navigation</span>
+        <mwc-list>
+          <mwc-list-item>Home</mwc-list-item>
+          <mwc-list-item>Search</mwc-list-item>
+          <mwc-list-item>Results</mwc-list-item>
+        </mwc-list>
         <div slot="appContent">
           <mwc-top-app-bar>
             <div slot="title">Title</div>
Unsere Applikation sollte nun etwa so aussehen:

Sidebar ein- und ausblenden
Die <mwc-drawer> Komponente kennt ein open Attribut / Property. Damit können wir die Sidebar als ein- oder ausgeblendet darstellen. Den Einblende-Zustand der Sidebar speichern wir in einem Property im AmenityFinder. Diesen Wert können wir dann jeweils an das open Property des <mwc-drawer> «binden». Immer wenn der Einblende-Zustand ändert, wird auch der neue Wert an <mwc-drawer> übergeben.
Zum Schluss brauchen wir noch ein Element, mit dem der User im UI den Einblende-Zustand ändern kann. Heute weit verbreitet für dieses Pattern ist der «Hamburger Button». Auch hier bietet uns MWC eine fertige Komponente, die wir verwenden können: <mwc-icon-button>.
Hamburger Button einbauen
Zuerst installieren wir die notwendige <mwc-icon-button> Komponente als Dependency in unserem Projekt
$ npm install @material/mwc-icon-button@^0.22.1und importieren diese in unsere AmenityFinder Klasse
import '@material/mwc-icon-button';Danach können wir das <mwc-icon-button> Custom Element entsprechend der Dokumentation verwenden. Wir möchten den Hamburger Button in der <mwc-top-app-bar> links vom Titel anzeigen. Dafür müssen wir den Button lediglich in den navigationIcon Slot der <mwc-top-app-bar> Komponente einbauen. Welches Icon im Button angezeigt wird, steuern wir mit dem icon Attribut und suchen noch den richtigen Namen aus der Liste aller verfügbarer Material Icons aus:
--- src/AmenityFinder.js
+++ src/AmenityFinder.js
         </mwc-list>
         <div slot="appContent">
           <mwc-top-app-bar>
+            <mwc-icon-button
+              icon="menu"
+              slot="navigationIcon"
+            ></mwc-icon-button>
             <div slot="title">Title</div>
           </mwc-top-app-bar>
           <div>
Der eingebaute Hamburger Button sieht dann etwa so aus:

Zustand der Sidebar abbilden
Unsere Applikation hat jetzt zwar eine Sidebar und einen Hamburger Button im Layout, diese «machen» allerdings noch nichts. Den auf- oder zugeklappten Zustand der Sidebar können wir in unserer Applikation mit einem Property abbilden. Wir definieren ein showSidebar property wie folgt:
static get properties() {
  return {
    showSidebar: {
      type: Boolean
    }
  };
}Default-Werte für Properties werden in Lit im Konstruktor definiert:
constructor() {
  super();
  this.showSidebar = false;
}Den Zustand (State) für die Sidebar haben wir nun definiert, jetzt müssen wir ihn noch verwenden. Unsere Sidebar wird als Inhalt von <mwc-drawer> dargestellt. Diese MWC Komponente kennt ein open Property. Das hört sich vielversprechend an und scheint genau das zu sein, was wir brauchen.
Properties können wir in Lit mit der Syntax .value="${...}" binden. In unserem Fall binden wir also das open Property von <mwc-drawer> auf unser Property showSidebar wie folgt:
<mwc-drawer hasHeader .open="${this.showSidebar}">Nun stellen wir fest, dass die Sidebar nach wie vor sichtbar ist. Die <mwc-drawer> kann ohne zusätzliche Konfiguration die Sidebar nicht verstecken. Entsprechend müssen wir dies über das type Attribut konfigurieren. Hier haben wir zwei Möglichkeiten: entweder dismissible oder modal. Ihr könnt beide ausprobieren und euch für eine Variante entscheiden:
<mwc-drawer hasHeader type="modal" .open="${this.showSidebar}">Damit haben wir unsere Sidebar versteckt:

Zustand der Sidebar durch Interaktion anpassen
Setzen wir nun im Code unser showSidebar Property auf true, sollte die Sidebar wieder Sichtbar sein. Nun brauchen wir noch einen Mechanismus, durch welchen wir den Wert des showSidebar Property über eine Interaktion im UI ändern können. Dafür bedienen wir uns eines weiteren nativen Browser-Mechanismus: DOM Events.
Wir wollen, dass bei jedem Klick der Zustand der Sidebar sich umkehrt. Der eingebaute Hamburger Button <mwc-icon-button> listet in der Dokumentation zwar keine Events auf, allerdings ist die MWC Komponente für den Browser einfach ein natives DOM-Element und wir können ganz normal auf einen Klick-Event einen Listener binden.
In Lit geschieht dies durch die Syntax @event="${...}". So können wir unseren Klick-Handler auf dem <mwc-icon-button> wie folgt registrieren:
--- src/AmenityFinder.js
+++ src/AmenityFinder.js
             <mwc-icon-button
               icon="menu"
               slot="navigationIcon"
+              @click="${() => {
+                this.showSidebar = !this.showSidebar;
+              }}"
             ></mwc-icon-button>
             <div slot="title">Title</div>
           </mwc-top-app-bar>
Et voilà! Wir sollten nun unsere Sidebar durch den Hamburger Button ein- und ausblenden können.
type="modal" vs type="dismissible" 
Die <mwc-drawer> Komponente unterscheidet zwischen zwei Typen: modal und dismissible. Falls ihr euch für den Typ modal entschieden habt, wird euch vermutlich noch ein Fehler im UI auffallen. Bei diesem Typ wird die Sidebar über den Inhalt gelegt, statt diesen auf die Seite zu schieben. In diesem Fall kann die modale Sidebar aber auch durch einen Klick ausserhalb der Sidebar wieder geschlossen werden. Der Zustand in unserer Applikation (über das showSidebar Property) wird so allerdings nicht mehr mit dem Zustand im Browser synchronisiert.
Glücklicherweise teilt uns <mwc-drawer> diese Zustandsänderung über den MDCDrawer:closed Event mit. Wir müssen nun also noch für diesen Fall den Zustand der Sidebar mit dem Wert unseres showSidebar Property synchronisieren. Wir binden uns auf den MDCDrawer:closed Event und setzen das showSidebar Property dabei jeweils auf false:
--- src/AmenityFinder.js
+++ src/AmenityFinder.js
   render() {
     return html`
-      <mwc-drawer hasHeader type="modal" .open="${this.showSidebar}">
+      <mwc-drawer
+        hasHeader
+        type="modal"
+        .open="${this.showSidebar}"
+        @MDCDrawer:closed="${() => {
+          this.showSidebar = false;
+        }}"
+      >
         <span slot="title">Navigation</span>
         <mwc-list>
           <mwc-list-item>Home</mwc-list-item>
Gut gemacht!
Du hast den zweiten Teil der Amenity Finder Serie erfolgreich gemeistert. In diesem Teil haben wir das Basis-Layout für die Amenity Finder Applikation implementiert. Du hast HTML- und CSS-Elemente verwendet, um das Layout zu strukturieren und das Aussehen der App zu gestalten. Gleichzeitig haben wir erste Erfahrungen mit Webkomponenten von Material Design gesammelt. Jetzt haben wir eine solide Grundlage, um mit der Umsetzung der funktionalen Anforderungen fortzufahren. Im dritten Teil werden wir die Applikation in einzelne Seiten unterteilen und das Routing implementieren.