====== LU10 – Komponenten, v-for und Props ====== ===== Lernziele ===== * Sie können ein Array of Objects erstellen und im Template ausgeben. * Sie können mit ''v-for'' dynamisch über ein Array iterieren und Komponenten rendern. * Sie können Daten via Props von einer Eltern- an eine Kind-Komponente übergeben. ===== Repetition: Was wir in LU09 gelernt haben ===== ==== Video: ref(), Mustache-Syntax und Komponentenstruktur ==== {{:de:modul:m291:learningunits:lu10:theorie:lu10_en_refs_moustache.mp4?1040x585|Video: ref, Mustache und script/template}} \\ //Aus dem Vue Mastery Crash Course (Englisch, deutsche Untertitel, 2:35 Min.)// \\ Das Video zeigt, wie eine Vue-Komponente aufgebaut ist, was ''ref()'' macht und wie die Mustache-Syntax ''%%{{ }}%%'' Daten im Template anzeigt. Eine kurze Übersicht des Gelernten: ^ Konzept ^ Was es macht ^ Beispiel ^ | ''ref()'' | Erstellt eine reaktive Variable | ''const isOpen = ref(false)'' | | ''%%{{ }}%%'' | Zeigt einen JS-Wert im Template an | ''%%{{ faqTitle }}%%'' | | '':'' (v-bind) | Bindet ein HTML-Attribut an einen JS-Wert | '':class="{ open: isOpen }"'' | | ''@'' (v-on) | Reagiert auf ein Event | ''@click="toggleOpen"'' | ===== Von einem Objekt zu einem Array of Objects ===== In LU09 haben wir die Frage und Antwort in einem einzelnen Objekt gespeichert: // LU09 – ein einzelnes Objekt const faqData = { question: 'What is this project?', answer: "It's a small but mighty mission..." } Für ein echtes Accordion mit mehreren Fragen brauchen wir ein **Array von Objekten**: import { ref } from 'vue'; const faqItems = ref([ { id: 1, question: 'What is this project, and how will it help me?', answer: "It's a small but mighty mission..." }, { id: 2, question: 'Is this free?', answer: 'Yes. No coins, no secret handshake...' }, { id: 3, question: 'Can I use this project in my portfolio?', answer: 'Absolutely. Show it off proudly...' }, ]) Jedes Objekt hat drei Eigenschaften: ''id'', ''question'' und ''answer''. Die ''id'' brauchen wir später für das ''key''-Attribut bei ''v-for''. ===== v-for: Dynamisch über ein Array iterieren ===== ==== Video: v-for und Arrays ==== {{:de:modul:m291:learningunits:lu10:theorie:lu10_en_array_v-for.mp4?1040x585|Video: v-for und Arrays}} \\ //Aus dem Vue Mastery Crash Course (Englisch, deutsche Untertitel, 2:52 Min.)// \\ **Kontext:** Im Video wird ein fiktiver Online-Shop für Socken als Beispiel verwendet. Das gezeigte Konzept – ''v-for'' über ein Array – funktioniert aber identisch in unserem FAQ Accordion. Die Parallele: ^ Socks-App ^ Unser Accordion ^ | ''detail in details'' | ''item in faqItems'' | | ''%%{{ detail }}%%'' | ''%%{{ item.question }}%%'' | ''v-for'' funktioniert wie eine ''forEach''-Schleife in JavaScript – nur direkt im HTML: // JavaScript forEach faqItems.forEach(item => { console.log(item.question); });
{{ item.question }}
Vue rendert für jedes Element im Array automatisch ein neues ''%%
%%''. Fügen wir dem Array ein neues Objekt hinzu, erscheint sofort ein neues Element im Browser. ==== Das key-Attribut ==== Bei ''v-for'' sollte jedes Element einen eindeutigen Schlüssel erhalten. Das hilft Vue, die Elemente effizient zu verwalten:
{{ item.question }}
'':key'' braucht einen eindeutigen Wert – idealerweise eine ID aus den Daten oder der Datenbank. ===== Das Problem mit einem gemeinsamen State ===== Wenn wir ''v-for'' direkt im Accordion verwenden und ''isOpen'' im Parent gesetzt wird, teilen alle Items denselben Zustand – klickt man auf eine Frage, reagieren alle gleichzeitig:
{{ item.answer }}
Die Lösung: Jede Frage-Antwort-Einheit bekommt ihren **eigenen State** – durch eine eigene Komponente. ===== Komponenten: Wiederverwendbare Bausteine ===== Eine **Komponente** ist ein abgeschlossener UI-Baustein mit eigenem Template, eigenem Script und eigenem Style. Sie kann beliebig oft verwendet werden. {{:de:modul:m291:learningunits:lu10:theorie:components.png?direct&1100|Eltern-Kind-Beziehung zwischen Accordion und AccordionItem}} Jede ''AccordionItem''-Komponente verwaltet ihr ''isOpen'' selbst. ===== Props: Daten vom Parent zum Kind ===== {{:de:modul:m291:learningunits:lu10:theorie:props_parent-to-child.png?direct&1100| Props von Eltern an Kind-Komponenten übergeben}} Mit **Props** können wir Daten von einer Eltern-Komponente an eine Kind-Komponente übergeben. ==== Props empfangen: defineProps() ==== In der Kind-Komponente deklarieren wir, welche Props wir erwarten: // AccordionItem.vue – ==== Accordion.vue ==== ===== Zusätzliche Videos zur Vertiefung ===== ^ Video ^ Inhalt ^ Dauer ^ | [[https://bzzch-my.sharepoint.com/:v:/g/personal/guido_koch_bzz_ch/IQBNpKK0Qb0WR5dxLs4ZDILDAbi3qj9tlM66LLyfAWXsbu0?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=KMu230|Vue Mastery (en mit de Untertitel) – v-bind]] | Attribute dynamisch binden | 3:22 Min. | | [[https://bzzch-my.sharepoint.com/:v:/g/personal/guido_koch_bzz_ch/IQDU62KDSK9fS5KJ_JfcWppFAVkv-KLPGe_rt5SbSxJcDg4?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=Um3BYj|Vue Mastery (en mit de Untertitel) – v-on]] | Events und EventListener | 2:42 Min. | | [[https://bzzch-my.sharepoint.com/:v:/g/personal/guido_koch_bzz_ch/IQDizvMGrec_TLgbV-KmUVtHAQj7qx_TYJxINcaZpuSMLeE?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=hwU3dA|Vue Mastery (en mit de Untertitel) – Style & Class Binding]] | Klassen und Styles dynamisch setzen | 4:50 Min. | | [[https://bzzch-my.sharepoint.com/:v:/g/personal/guido_koch_bzz_ch/IQAQ4UIL-p7gSJSec-ZJe7aEAWLIS4SMpwl-DxoZIlSCf-Q?nav=eyJyZWZlcnJhbEluZm8iOnsicmVmZXJyYWxBcHAiOiJPbmVEcml2ZUZvckJ1c2luZXNzIiwicmVmZXJyYWxBcHBQbGF0Zm9ybSI6IldlYiIsInJlZmVycmFsTW9kZSI6InZpZXciLCJyZWZlcnJhbFZpZXciOiJNeUZpbGVzTGlua0NvcHkifX0&e=CpBfRy|Scrimba – Projekt setup]] | Projekt anlegen mit create-vue | 3:07 Min. |