HTML5-Tutorium: JavaScript: Hello World Vue 04: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Kowa (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Kowa (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Zeile 25: Zeile 25:


Erstellen Sie einen neuen Projektzweig (branch) innerhalb von <code>hello_world_vue</code> und fügen Sie das Package <code>uuid</code> hinzu:
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von <code>hello_world_vue</code> und fügen Sie das Package <code>uuid</code> hinzu:
 
<syntaxhighlight lang="bash">
<source lang="bash">
git checkout v03    # Wechsle in den Branch v03
git checkout v03    # Wechsle in den Branch v03
git checkout -b v04  # Klone v03 in einen neuen Branch v04
git checkout -b v04  # Klone v03 in einen neuen Branch v04
npm i
npm i
npm i uuid
npm i uuid
</source>
</syntaxhighlight>


== Erstellen der Hilfskomponenten ==
== Erstellen der Hilfskomponenten ==
Zeile 39: Zeile 38:
Eine parametrisierbare Button-Komponente.
Eine parametrisierbare Button-Komponente.


<source lang="html">
<syntaxhighlight lang="html">
<!-- src/component/form/FormButton.vue -->
<!-- src/component/form/FormButton.vue -->


Zeile 60: Zeile 59:
   @import '/css/component/form/FormButton';
   @import '/css/component/form/FormButton';
</style>
</style>
</source>
</syntaxhighlight>


Das zugehörige CSS-Modul importiert lediglich ein CSS-Modul für Formulare.
Das zugehörige CSS-Modul importiert lediglich ein CSS-Modul für Formulare.


<source lang="css">
<syntaxhighlight lang="css">
// src/css/component/form/FormButton.css
// src/css/component/form/FormButton.css


@import 'Form';
@import 'Form';
</source>
</syntaxhighlight>


Das Formular-CSS-Modul definiert Eigenschaften, die sich mehrere Formular-Elemente teilen.
Das Formular-CSS-Modul definiert Eigenschaften, die sich mehrere Formular-Elemente teilen.


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/form/_Form.css */
/* src/css/component/form/_Form.css */


Zeile 92: Zeile 91:
label
label
{ text-align: right; }
{ text-align: right; }
</source>
</syntaxhighlight>


=== <code>FormTextfield.vue</code> ===
=== <code>FormTextfield.vue</code> ===


Eine parametrisierbare Textfield-Komponente zum Erfassen von Texteingaben.
Eine parametrisierbare Textfield-Komponente zum Erfassen von Texteingaben.
<source lang="html">
<syntaxhighlight lang="html">
// src/component/form/FormTextfield.vue
// src/component/form/FormTextfield.vue


Zeile 135: Zeile 134:
   @import '/css/component/form/FormTextfield';
   @import '/css/component/form/FormTextfield';
</style>
</style>
</source>
</syntaxhighlight>


Mit Hilfe von "computed" wird für die Property "text" bidirektionales Binding ermöglicht.
Mit Hilfe von "computed" wird für die Property "text" bidirektionales Binding ermöglicht.
Zeile 145: Zeile 144:
Wie zuvor wird das Formular-CSS-Modul verwendet.
Wie zuvor wird das Formular-CSS-Modul verwendet.


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/form/FormTextfield.css */
/* src/css/component/form/FormTextfield.css */


@import 'Form';
@import 'Form';
</source>
</syntaxhighlight>


== Erstellen der Hello-World-Section-Komponenten ==
== Erstellen der Hello-World-Section-Komponenten ==
Zeile 157: Zeile 156:
<code>SectionForm</code> enthält nur noch ein <code>section</code>-Element. Die Buttons und das Textfield werden als Komponenten eingebunden.
<code>SectionForm</code> enthält nur noch ein <code>section</code>-Element. Die Buttons und das Textfield werden als Komponenten eingebunden.


<source lang="html">
<syntaxhighlight lang="html">
// src/component/section/SectionForm.vue
// src/component/section/SectionForm.vue


Zeile 193: Zeile 192:
   @import '/css/component/section/SectionForm';
   @import '/css/component/section/SectionForm';
</style>
</style>
</source>
</syntaxhighlight>


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/section/SectionForm.css */
/* src/css/component/section/SectionForm.css */


Zeile 208: Zeile 207:
   { margin-bottom: 0.5ex; }
   { margin-bottom: 0.5ex; }
}
}
</source>
</syntaxhighlight>


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/section/_Section.css */
/* src/css/component/section/_Section.css */


Zeile 222: Zeile 221:
   }
   }
}
}
</source>
</syntaxhighlight>


=== <code>SectionHello.vue</code> ===
=== <code>SectionHello.vue</code> ===


<source lang="html">
<syntaxhighlight lang="html">
// src/component/section/SectionHello.vue
// src/component/section/SectionHello.vue


Zeile 248: Zeile 247:
   @import '/css/component/section/SectionHello';
   @import '/css/component/section/SectionHello';
</style>
</style>
</source>
</syntaxhighlight>


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/section/SectionHello.css */
/* src/css/component/section/SectionHello.css */


Zeile 259: Zeile 258:
   { font-size: $font-size-p-large !important; }
   { font-size: $font-size-p-large !important; }
}
}
</source>
</syntaxhighlight>


== Modularisieren der Hello-World-Komponente ==
== Modularisieren der Hello-World-Komponente ==
Zeile 269: Zeile 268:
Dies kann später entfallen, wenn die Stores mit Hilfe von JSON-Dateien initialisiert werden.
Dies kann später entfallen, wenn die Stores mit Hilfe von JSON-Dateien initialisiert werden.


<source lang="html">
<syntaxhighlight lang="html">
// src/component/HelloWorld.vue
// src/component/HelloWorld.vue


Zeile 289: Zeile 288:
   @import '/css/component/HelloWorld.css';
   @import '/css/component/HelloWorld.css';
</style>
</style>
</source>
</syntaxhighlight>


Das Folgende ist nur eine Dummy-CSS-Datei, die aus Symmetriegründen
Das Folgende ist nur eine Dummy-CSS-Datei, die aus Symmetriegründen
Zeile 295: Zeile 294:
gemäß WK-Konvention eine eigene CSS-Datei.
gemäß WK-Konvention eine eigene CSS-Datei.


<source lang="css">
<syntaxhighlight lang="css">
/* src/css/component/HelloWorld.css */
/* src/css/component/HelloWorld.css */


@import 'config';
@import 'config';
</source>
</syntaxhighlight>


== Bereinigen der Datei <code>body.css</code> ==
== Bereinigen der Datei <code>body.css</code> ==
Zeile 310: Zeile 309:
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.
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">
<syntaxhighlight lang="javascript">
import { defineStore } from 'pinia'
import { defineStore } from 'pinia'
import { ref }        from 'vue'
import { ref }        from 'vue'
Zeile 331: Zeile 330:


export default storeSection
export default storeSection
</source>
</syntaxhighlight>


'''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.
Zeile 337: Zeile 336:
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.
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">
<syntaxhighlight lang="html">
<template>
<template>
   <SectionForm  v-if="section.currentSection==='form'"/>
   <SectionForm  v-if="section.currentSection==='form'"/>
Zeile 355: Zeile 354:
   ...
   ...
</template>
</template>
</source>
</syntaxhighlight>


wird zu  
wird zu  


<source lang="html">
<syntaxhighlight lang="html">
<script setup>
<script setup>
   ...
   ...
Zeile 369: Zeile 368:
   ...
   ...
</template>
</template>
</source>
</syntaxhighlight>


Die zweite Section <code>SectionHello</code> muss entsprechend angepasst werden. Hier wird <code>StoreSection</code> gar nicht mehr benötigt.
Die zweite Section <code>SectionHello</code> muss entsprechend angepasst werden. Hier wird <code>StoreSection</code> gar nicht mehr benötigt.

Version vom 9. April 2025, 12:16 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)

Vorlesung WebProg

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:

  • component/section/SectionHello.vue
  • component/section/SectionForm.vue

Zuvor sollten Sie zwei wiederverwendbare Hilfskomponenten definieren:

  • component/form/FormButton.vue
  • component/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/component/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>
  @import '/css/component/form/FormButton';
</style>

Das zugehörige CSS-Modul importiert lediglich ein CSS-Modul für Formulare.

// src/css/component/form/FormButton.css

@import 'Form';

Das Formular-CSS-Modul definiert Eigenschaften, die sich mehrere Formular-Elemente teilen.

/* src/css/component/form/_Form.css */

@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/component/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>
  @import '/css/component/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-CSS-Modul verwendet.

/* src/css/component/form/FormTextfield.css */

@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/component/section/SectionForm.vue

<script setup>
  import FormButton    from '/component/form/FormButton.vue'
  import FormTextfield from '/component/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>
  @import '/css/component/section/SectionForm';
</style>
/* src/css/component/section/SectionForm.css */

@import 'Section';

section
{ text-align:   center;
  margin-left:  auto;
  margin-right: auto;

  h1
  { margin-bottom: 0.5ex; }
}
/* src/css/component/section/_Section.css */

@import 'config';

section
{ h1
  { font-size: $font-size-h1;
    padding:   0;
    margin:    0;
  }
}

SectionHello.vue

// src/component/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>
  @import '/css/component/section/SectionHello';
</style>
/* src/css/component/section/SectionHello.css */

@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/component/HelloWorld.vue

<script setup>
  import SectionForm  from '/component/section/SectionForm.vue'
  import SectionHello from '/component/section/SectionHello.vue'

  import storeSection from '/store/StoreSection'
  const section = storeSection()
  section.init('form')
</script>

<template>
  <SectionForm/>
  <SectionHello/>
</template>

<style scoped>
  @import '/css/component/HelloWorld.css';
</style>

Das Folgende ist nur eine Dummy-CSS-Datei, die aus Symmetriegründen angelegt wurde: Für jede Komponente gibt es gemäß WK-Konvention eine eigene CSS-Datei.

/* src/css/component/HelloWorld.css */

@import 'config';

Bereinigen der Datei body.css

Die in den obigen CSS-Modulen enthaltenen Inhalte können und sollten aus der Datei body.css 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>
</source>

Nun kann <code>visiblity</code> aus den beiden Komponenten <code>SectionForm</code> und <code>SectionHello</code> entfernt werden.
<source lang="html">
<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

  1. Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)