HTML5-Tutorium: JavaScript: Hello World Vue 04: Unterschied zwischen den Versionen
Kowa (Diskussion | Beiträge) |
Kowa (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
(15 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}} | {{HTML5-Tutorium:JavaScript:HelloWorld:Menü}} | ||
'''Musterlösung'''<br/> | '''Musterlösung'''<br/> | ||
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], <code>git checkout v04 | [{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], <code>git checkout v04</code><br> | ||
== Anwendungsfälle (Use Cases) == | == Anwendungsfälle (Use Cases) == | ||
Zeile 56: | Zeile 54: | ||
<template> | <template> | ||
<input :value="label" :type | <input :value="label" :type @click="click"/> | ||
</template> | </template> | ||
Zeile 110: | Zeile 108: | ||
props = defineProps | props = defineProps | ||
({ label: { type: String, default: '' }, | ({ label: { type: String, default: '' }, | ||
text: { type: String, default: '' } | text: { type: String, default: '' }, | ||
autofocus: { type: Boolean, default: false } | |||
}), | }), | ||
id = uuid(), | id = uuid(), | ||
Zeile 128: | Zeile 127: | ||
<div> | <div> | ||
<label :for="id" v-if="label !== ''">{{label}}</label> | <label :for="id" v-if="label !== ''">{{label}}</label> | ||
<input :id="id" type="text" v-model="text" :autofocus="autofocus" | <input :id type="text" v-model="text" :autofocus> | ||
<!--<input :id="id" type="text" v-model="text" :autofocus="autofocus">--> | |||
</div> | </div> | ||
</template> | </template> | ||
Zeile 170: | Zeile 170: | ||
section = storeSection(), | section = storeSection(), | ||
greeting = storeGreeting(), | greeting = storeGreeting(), | ||
sayHello = () => | sayHello = () => section.change('hello') | ||
</script> | </script> | ||
<template> | <template> | ||
<section | <section v-bind:class="section.visibility('form')"> | ||
<h1>{{greeting.helloStranger}}</h1> | <h1>{{greeting.helloStranger}}</h1> | ||
<form> | <form> | ||
Zeile 199: | Zeile 199: | ||
@import 'Section'; | @import 'Section'; | ||
section | |||
{ text-align: center; | |||
margin-left: auto; | |||
margin-right: auto; | |||
h1 | |||
{ margin-bottom: 0.5ex; } | |||
} | |||
</source> | </source> | ||
Zeile 207: | Zeile 216: | ||
section | section | ||
{ | { h1 | ||
{ font-size: $font-size-h1; | |||
padding: 0; | |||
margin: 0; | |||
{ font-size: | |||
margin | |||
} | } | ||
} | } | ||
Zeile 233: | Zeile 239: | ||
<template> | <template> | ||
<section | <section v-bind:class="section.visibility('hello')"> | ||
<h1>{{greeting.hello}}</h1> | <h1>{{greeting.hello}}</h1> | ||
<p>Welcome to Web Programming!</p> | <p>Welcome to Web Programming!</p> | ||
Zeile 281: | Zeile 287: | ||
<style scoped lang="scss"> | <style scoped lang="scss"> | ||
@import '/css/components/HelloWorld.scss' | @import '/css/components/HelloWorld.scss'; | ||
</style> | </style> | ||
</source> | </source> | ||
Zeile 300: | Zeile 306: | ||
aus der Datei <code>body.scss</code> gelöscht werden. | aus der Datei <code>body.scss</code> gelöscht werden. | ||
== Vereinfachung von <code> | == Vereinfachung von <code>StoreSection</code> == | ||
Die Funktion <code> | Die Funktion <code>visibility</code> zum Überprüfen, ob eine Section angezeigt werden soll, kann durch Vue-Attribute <code>v-if</code> ersetzt werden. Dadurch vereinfacht sich der code von deutlich. | ||
<source lang="javascript"> | <source lang="javascript"> | ||
Zeile 329: | Zeile 335: | ||
'''Anmerkung''': Sobal die Initialisierung über eine globale Konfigurationsdatei erfolgt, können wir die Initialisierungsfuktion auch noch entfernen. | '''Anmerkung''': Sobal die Initialisierung über eine globale Konfigurationsdatei erfolgt, können wir die Initialisierungsfuktion auch noch entfernen. | ||
Die Kompontente <code>HelloWorld.vue</code>, die bislang auf die Arbeit der Funktion <code> | Die Kompontente <code>HelloWorld.vue</code>, die bislang auf die Arbeit der Funktion <code>visibility</code> vertraut, muss entsprechend angepasst werden. Sie testet mittels <code>v_if</code> welche Section angezeigt werden muss. | ||
<source lang="html"> | <source lang="html"> | ||
Zeile 338: | Zeile 344: | ||
</source> | </source> | ||
Nun kann <code> | Nun kann <code>visiblity</code> aus den beiden Komponenten <code>SectionForm</code> und <code>SectionHello</code> entfernt werden. | ||
<source lang="html"> | <source lang="html"> | ||
<script setup> | |||
... | ... | ||
sayHello = () => section.change('hello') | sayHello = () => section.change('hello') | ||
Zeile 345: | Zeile 352: | ||
<template> | <template> | ||
<section | <section v-bind:class="section.visibility('form')"> | ||
... | |||
</template> | |||
</source> | </source> | ||
Zeile 351: | Zeile 360: | ||
<source lang="html"> | <source lang="html"> | ||
<script setup> | |||
... | ... | ||
sayHello = () => section.currentSection = 'hello' | sayHello = () => section.currentSection = 'hello' | ||
Zeile 356: | Zeile 366: | ||
<template> | <template> | ||
<section | <section> | ||
... | ... | ||
</template> | |||
</source> | </source> | ||
Die zweite Section <code>SectionHello</code> muss entsprechend angepasst werden. Hier wird <code>StoreSection</code> gar nicht mehr benötigt. | |||
== Fortsetzung des Tutoriums == | == Fortsetzung des Tutoriums == |
Aktuelle Version vom 13. Mai 2024, 11:09 Uhr
Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:
Korrektheit: 3 (zu größeren Teilen überprüft) |
Umfang: 4 (unwichtige Fakten fehlen) |
Quellenangaben: 3 (wichtige Quellen vorhanden) |
Quellenarten: 5 (ausgezeichnet) |
Konformität: 3 (gut) |
Inhalt | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6 | Vue 1 | Vue 2 | Vue 3 | Vue 4 | Vue 5 | Vue 6
Musterlösung
Git-Repository, git checkout v04
Anwendungsfälle (Use Cases)
Gegenüber dem ersten, zweiten und dritten Teil des Vue-Tutoriums ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.
Aufgabe
Aufgabe: Modularisieren Sie HelloWorld.vue
Definieren Sie für die Komponente HelloWorld.vue
zwei Subkomponenten:
components/section/SectionHello.vue
components/section/SectionForm.vue
Zuvor sollten Sie zwei wiederverwendbare Hilfskomponenten definieren:
components/form/FormButton.vue
components/form/FormTextfield.vue
Erstellen eines neuen Projektzweigs
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von hello_world_vue
und fügen Sie das Package uuid
hinzu:
git checkout v03 # Wechsle in den Branch v03
git checkout -b v04 # Klone v03 in einen neuen Branch v04
npm i
npm i uuid
Erstellen der Hilfskomponenten
FormButton.vue
Eine parametrisierbare Button-Komponente.
<!-- src/components/form/FormButton.vue -->
<script setup>
defineProps
({ type: { type: String, default: 'button' },
label: { type: String, default: 'Button' }
})
const
emit = defineEmits(['click']),
click = () => emit('click')
</script>
<template>
<input :value="label" :type @click="click"/>
</template>
<style scoped lang="scss">
@import '/css/components/form/FormButton';
</style>
Das zugehörige SCSS-Modul importiert lediglich ein SCSS-Modul für Formulare.
// src/css/components/form/FormButton.scss
@import 'Form';
Das Formular-SCSS-Modul definiert Eigenschaften, die sich mehrere Formular-Elemente teilen.
/* src/css/components/form/_Form.scss */
@import 'config';
label
{ font-family: $font-family-serif;
font-size: $font-size-p !important;
}
label, input
{ width: 10em;
font-size: 100%;
display: inline-block;
box-sizing: border-box;
margin: 0.5ex;
}
label
{ text-align: right; }
FormTextfield.vue
Eine parametrisierbare Textfield-Komponente zum Erfassen von Texteingaben.
// src/components/form/FormTextfield.vue
<script setup>
import { computed, onMounted, onUnmounted } from 'vue'
import { v4 as uuid } from 'uuid'
import ControllerKey from '@/controller/ControllerKey'
const
props = defineProps
({ label: { type: String, default: '' },
text: { type: String, default: '' },
autofocus: { type: Boolean, default: false }
}),
id = uuid(),
emit = defineEmits(['update:text', 'enter']),
text = computed
({ get() { return props.text },
set(p_value) { return emit('update:text', p_value) }
}),
enter = () => emit('enter'),
controllerKey = new ControllerKey('Enter', enter, id)
onMounted (() => controllerKey.add())
onUnmounted(() => controllerKey.remove())
</script>
<template>
<div>
<label :for="id" v-if="label !== ''">{{label}}</label>
<input :id type="text" v-model="text" :autofocus>
<!--<input :id="id" type="text" v-model="text" :autofocus="autofocus">-->
</div>
</template>
<style scoped lang="scss">
@import '/css/components/form/FormTextfield';
</style>
Mit Hilfe von "computed" wird für die Property "text" bidirektionales Binding ermöglicht.
Die Getterfunktion liest den Wert aus der Property des Aufrufers. Die Setterfunktion
erzeugt bei jeder Änderung ein passendes Ereignis. Der Name des Ereignisse muss laut Vue-Konvention
'update:<Propertyname>'
lauten.
Wie zuvor wird das Formular-SCSS-Modul verwendet.
/* src/css/components/form/FormTextfield.scss */
@import 'Form';
Erstellen der Hello-World-Section-Komponenten
SectionForm.vue
SectionForm
enthält nur noch ein section
-Element. Die Buttons und das Textfield werden als Komponenten eingebunden.
// src/components/section/SectionForm.vue
<script setup>
import FormButton from '@/components/form/FormButton.vue'
import FormTextfield from '@/components/form/FormTextfield.vue'
import storeSection from '@/store/StoreSection'
import storeGreeting from '@/store/StoreGreeting'
const
section = storeSection(),
greeting = storeGreeting(),
sayHello = () => section.change('hello')
</script>
<template>
<section v-bind:class="section.visibility('form')">
<h1>{{greeting.helloStranger}}</h1>
<form>
<div>
<FormTextfield label="What's your name?" autofocus
v-model:text="greeting.name" @enter="sayHello"
/>
</div>
<div>
<FormButton label="Reset" type="reset" />
<FormButton label="Say Hello" @click="sayHello"/>
</div>
</form>
</section>
</template>
<style scoped lang="scss">
@import '/css/components/section/SectionForm';
</style>
/* src/css/components/section/SectionForm.scss */
@import 'Section';
section
{ text-align: center;
margin-left: auto;
margin-right: auto;
h1
{ margin-bottom: 0.5ex; }
}
/* src/css/components/section/_Section.scss */
@import 'config';
section
{ h1
{ font-size: $font-size-h1;
padding: 0;
margin: 0;
}
}
SectionHello.vue
// src/components/section/SectionHello.vue
<script setup>
import storeSection from '@/store/StoreSection'
import storeGreeting from '@/store/StoreGreeting'
const
section = storeSection(),
greeting = storeGreeting()
</script>
<template>
<section v-bind:class="section.visibility('hello')">
<h1>{{greeting.hello}}</h1>
<p>Welcome to Web Programming!</p>
</section>
</template>
<style scoped lang="scss">
@import '/css/components/section/SectionHello';
</style>
/* src/css/components/section/SectionHello.scss */
@import 'Section';
section
{ p
{ font-size: $font-size-p-large !important; }
}
Modularisieren der Hello-World-Komponente
Nun besteht die Hello-World-Komponenten nur noch aus zwei Section-Komponenten.
Allerdings muss noch die Startkomponente, das heißt die Komponenten, die zunächst sichtbar sein soll, initialisiert werden.
Dies kann später entfallen, wenn die Stores mit Hilfe von JSON-Dateien initialisiert werden.
// src/components/HelloWorld.vue
<script setup>
import SectionForm from '@/components/section/SectionForm.vue'
import SectionHello from '@/components/section/SectionHello.vue'
import storeSection from '@/store/StoreSection'
const section = storeSection()
section.init('form')
</script>
<template>
<SectionForm/>
<SectionHello/>
</template>
<style scoped lang="scss">
@import '/css/components/HelloWorld.scss';
</style>
Das Folgende ist nur eine Dummy-SCSS-Datei, die aus Symmetriegründen angelegt wurde: Für jede Komponente gibt es gemäß WK-Konvention eine eigene SCSS-Datei.
/* src/css/components/HelloWorld.scss */
@import 'config';
Bereinigen der Datei body.scss
Die in den obigen SCSS-Modulen enthaltenen Inhalte können und sollten
aus der Datei body.scss
gelöscht werden.
Vereinfachung von StoreSection
Die Funktion visibility
zum Überprüfen, ob eine Section angezeigt werden soll, kann durch Vue-Attribute v-if
ersetzt werden. Dadurch vereinfacht sich der code von deutlich.
import { defineStore } from 'pinia'
import { ref } from 'vue'
const storeSection =
defineStore
( 'section',
() =>
{ const
currentSection =
ref(''),
init =
p_section => currentSection.value = p_section
return { init, currentSection }
}
)
export default storeSection
Anmerkung: Sobal die Initialisierung über eine globale Konfigurationsdatei erfolgt, können wir die Initialisierungsfuktion auch noch entfernen.
Die Kompontente HelloWorld.vue
, die bislang auf die Arbeit der Funktion visibility
vertraut, muss entsprechend angepasst werden. Sie testet mittels v_if
welche Section angezeigt werden muss.
<template>
<SectionForm v-if="section.currentSection==='form'"/>
<SectionHello v-if="section.currentSection==='hello'"/>
</template>
Nun kann visiblity
aus den beiden Komponenten SectionForm
und SectionHello
entfernt werden.
<script setup>
...
sayHello = () => section.change('hello')
</script>
<template>
<section v-bind:class="section.visibility('form')">
...
</template>
wird zu
<script setup>
...
sayHello = () => section.currentSection = 'hello'
</script>
<template>
<section>
...
</template>
Die zweite Section SectionHello
muss entsprechend angepasst werden. Hier wird StoreSection
gar nicht mehr benötigt.
Fortsetzung des Tutoriums
Sie sollten nun Teil 5 des Vue-Tutoriums bearbeiten. In diesem Tutorium werden die Daten extern aus JSON-Dateien geladen, erst statisch, dann dynamisch. Zum Schluss wird die Anwendung internationalisiert.
Quellen
- Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)