Zum Inhalt
wannacode

Bedingungen: leer, Fehler, lädt

Kurz gesagt: Bedingtes Rendern zeigt nur den Teil des Markups, der zu den Daten passt (lädt, Fehler, leer oder die Liste); in Vue mit v-if, v-else und v-show.

Problem

Lädt, Fehler und die Liste gleichzeitig

Der Katalog ist geladen. Unter der Überschrift steht aber „Wird geladen…“, dann „Die Produkte wurden nicht geladen.“, dann „In dieser Kategorie gibt es keine Produkte.“, und erst danach kommen die Produkte.

Vue

Die Komponente Catalog bekommt status und die Produkte als Props. Aber sie schaut nicht darauf: Sie zeichnet immer alle vier Teile. Der Käufer weiß nicht, was er glauben soll, und ein Screenreader liest sowohl den Fehler als auch die Produkte vor.

Die Idee

Der Teil, der zu den Daten passt

Der Katalog hat vier Zustände: lädt, Fehler, leer und bereit. Nur der Teil für den aktuellen soll auf dem Bildschirm sein. Das ist bedingtes Rendern: Die Komponente entscheidet anhand der Daten, was sie zurückgibt.

In Vue sind die Bedingungen Attribute im Template:

<p v-if="status === 'loading'" role="status">Wird geladen…</p>
<p v-else-if="status === 'error'" role="alert">Die Produkte wurden nicht geladen.</p>
<p v-else-if="status === 'empty'">In dieser Kategorie gibt es keine Produkte.</p>
<ul v-else>…</ul>

v-if zeichnet das Element nur, wenn der Ausdruck wahr ist. v-else-if und v-else setzen die Kette fort und müssen direkt nach dem vorherigen Element der Kette stehen. Genau ein Element der Kette wird gezeichnet.

Die beiden Bibliotheken lösen dasselbe auf ihre Weise: React mit JavaScript um das Markup herum, Vue mit Attributen darin. Das Ergebnis ist dasselbe: Das Element einer falschen Bedingung ist nicht im DOM. Es ist nicht versteckt, es existiert nicht.

Hier wird status als Prop übergeben. Wie der Katalog ihn beim Laden aus der API selbst ändert, steht im Modul „Effekte, Daten und Formulare“.

Sieh es dir an

Sieh, welcher Zweig hineinkommt

Ein Wert geht in die Komponente, und nur ein Zweig kommt ins DOM. Achte bei jeder Änderung von status auf die Spalte „DOM“.

<template>
<p v-if="status === 'loading'">Wird geladen…</p>
<p v-else-if="status === 'error'">Fehler</p>
<p v-else-if="status === 'empty'">Keine Produkte</p>
<ul v-else>…</ul>
</template>
<!-- im DOM, aber versteckt -->
<p v-show="status === 'loading'">Wird geladen…</p>
Schritt für Schritt, in Worten
  1. status kommt als Prop in die Komponente. Er ist 'loading': v-if passt, und nur <p>Wird geladen…</p> wird gezeichnet. Die anderen Zweige sind gar nicht im DOM.
  2. status wird 'error'. Die Bibliothek zeichnet neu: Jetzt passt der zweite Zweig. <p>Wird geladen…</p> verlässt das DOM, und die Fehlermeldung kommt hinein.
  3. 'empty': nur die Meldung, dass die Kategorie leer ist.
  4. 'ready': Weder v-if noch v-else-if passt, deshalb wird v-else gezeichnet: die Liste. Im DOM ist immer ein Zweig.
  5. Der andere Weg: v-show zeichnet das Element immer und schaltet nur sein display um. Bei loading ist es sichtbar.
  6. status wird 'ready'. Das Element bleibt mit allem, was darin ist, im DOM und bekommt nur display: none. Niemand sieht es, und ein Screenreader überspringt es.
  7. v-if entfernt das Element aus dem DOM, v-show versteckt es nur. v-if ist der Normalfall; v-show ist für etwas, das oft umgeschaltet wird oder das Eingetippte behalten muss.

Der erste Teil ist bedingtes Rendern: Bei einem neuen Wert verlässt das alte Element das DOM, und das neue kommt hinein. Der zweite Teil ist Verstecken: Das Element ist die ganze Zeit im DOM und wird nur gezeigt oder versteckt. Darum geht es am Ende der Lektion.

Probier es aus

Die vier Zustände

Catalog zeichnet alles auf einmal. Baue es so um, dass jeder Abschnitt nur den Teil seines Zustands hat: „Wird geladen…“ bei lädt, den Fehler bei Fehler, „In dieser Kategorie gibt es keine Produkte.“ bei leer und die Liste bei bereit. Die anderen Teile dürfen nicht auf der Seite sein.

Vue

Probier es aus

Ergänze die Lücke

„Wird geladen…“ soll nur erscheinen, solange status loading ist.

<p ="status === 'loading'" role="status">Wird geladen…</p>

Die Idee

Ausverkauft: ein Abzeichen und ein deaktivierter Button

Eine Bedingung gibt es nicht nur für ganze Teile. In der Produktkarte ändert sie einen Text und ein Attribut:

<p v-if="product.stock === 0" class="badge">Ausverkauft</p>
<p v-else>Nur noch {{ product.stock }} übrig</p>
<button type="button" :disabled="product.stock === 0">Hinzufügen</button>

:disabled="true" setzt das Attribut disabled, und :disabled="false" entfernt es.

Ein deaktivierter Button lässt sich nicht drücken, bekommt mit Tab keinen Fokus, und ein Screenreader sagt, dass er nicht verfügbar ist. Das Abzeichen sagt, warum. Ein Button, den man drücken kann und der dann „nicht auf Lager“ sagt, ist schlechter: Der Käufer erfährt es erst nach dem Klick.

Probier es aus

Ausverkauft

Sorge dafür, dass ProductCard „Ausverkauft“ zeigt und den Button „Hinzufügen“ deaktiviert, wenn stock 0 ist. Sonst zeigt sie „Nur noch 5 übrig“, und der Button funktioniert. Der Käse ist ausverkauft.

Vue

Die Idee

Die Null auf dem Bildschirm

v-if schaut, ob der Wert wahr ist, also zeichnet v-if="items.length" bei einem leeren Warenkorb nichts. Die Null kommt heraus, wenn die Bedingung in {{ }} steht:

<p>{{ items.length && `Im Warenkorb: ${items.length}` }}</p> <!-- leerer Warenkorb: 0 -->
<p v-if="items.length > 0">Im Warenkorb: {{ items.length }}</p> <!-- leerer Warenkorb: nichts -->

&& gibt nicht true oder false zurück. Es gibt die linke Seite zurück, wenn sie falsy ist: hier 0, und {{ }} zeigt sie. {{ false }} zeigt sogar das Wort „false“. Schreib Bedingungen mit v-if, und heb {{ }} für Werte auf.

Probier es aus

Finde den Fehler

Bei einem leeren Warenkorb steht in der oberen Ecke „0“.

Wähle die Zeile mit dem Fehler.

Die Idee

Entfernt oder versteckt

Manchmal muss ein Element im DOM bleiben und nur nicht zu sehen sein. Hier haben beide Panels ein Feld für eine Notiz. Öffne beide, tipp etwas ein, schließ sie und öffne sie wieder.

Vue
Im ersten Abschnitt nutzt das Panel v-if: Geschlossen ist es weg, und geöffnet ist es neu, mit leerem Feld. Im zweiten nutzt es v-show: Es bleibt mit display: none im DOM und behält das Eingetippte.

Entfernen ist der Normalfall: weniger Elemente auf der Seite, nichts Verstecktes für einen Screenreader. Verstecken ist für Teile, die oft geöffnet und geschlossen werden oder behalten müssen, was darin eingegeben wurde.

Genauer, für Neugierige

v-if an <template>. Um mehrere Elemente unter einer Bedingung ohne Hülle zu zeigen, setz v-if an ein <template>: Es kommt nicht ins DOM. v-show funktioniert an <template> nicht, weil es kein Element gibt, dem man display: none geben könnte.

Der Preis. v-if baut und entfernt die Elemente bei jeder Änderung, während v-show sie einmal baut, beim ersten Zeichnen, und danach nur display umschaltet. v-show passt also zu Teilen, die sich oft ändern, und v-if zu Teilen, die selten gezeigt werden.

v-if und v-for. In Vue 3 läuft ein v-if am selben Element wie v-for zuerst und sieht die Schleifenvariable nicht. Setz das v-if an eine Hülle oder filtere die Daten.

Die Options API in altem Code nutzt dieselben v-if, v-else und v-show.

aria-hidden ist kein Verstecken. aria-hidden="true" versteckt das Element nur vor einem Screenreader, und es bleibt auf dem Bildschirm. Vor allen zu verstecken geht mit display: none: hidden, v-show oder Entfernen.

Übung

Der Warenkorb

App zeigt drei Warenkörbe: leer, mit Produkten und mit einem ausverkauften Produkt. Baue Cart so, dass der leere „Der Warenkorb ist leer.“ sagt, ohne Liste und ohne Summe, und der Button „Bestellen“ deaktiviert ist. Der volle zeigt die Zeilen, „Gesamt: 4,00 €“ und einen funktionierenden Button. Das ausverkaufte Produkt hat ein Abzeichen „Ausverkauft“, über dem Button steht „Entferne die ausverkauften Produkte, um zu bestellen.“ (Rolle alert), und der Button ist deaktiviert.

Vue

Test

Teste dich

1. Die Bedingung für „Wird geladen…“ ist falsch. Was steht an ihrer Stelle im DOM?
2. Der Warenkorb ist leer. Was zeigt <p>{{ items.length && 'Es gibt Produkte' }}</p>?
3. Was stimmt über v-else?
4. Ein Panel mit einem Notizfeld wird oft geöffnet und geschlossen, und das Eingetippte soll bleiben. Was passt besser?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·