HTML5-Tutorium: JavaScript: Hello World Vue 02

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg

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

Musterlösung
v02: index.html (HTML validate, CSS validate, Google PageSpeed)

Git-Repository, git checkout v02 (JavaScript)

Anwendungsfälle (Use Cases)

Gegenüber dem ersten Teil des Vue-Tutoriums ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.

Aufgabe

Ergänze einen Keyup-Event-Handler, so dass das Name mit Hilfe der Entertaste übergeben werden kann, wenn der Fokus auf dem Namenfeld liegt.

Erstellen eines neuen Projektzweigs

Erstellen Sie einen neuen Projektzweig (branch) innerhalb von hello_world_vue:

git checkout v01     # Wechsle in den Branch v01
git checkout -b v02  # Klone v01 in einen neuen Branch v02
npm i

MVC-Prozess

MVC-Module

Zur Lösung der Aufgabe wird eine eigene Controller-Klasse geschrieben.

Dieser Lösung liegt der MVC-Prozess zu Grunde: Die Anwendung besteht aus Model, Views und Controller.

Das Model enthalt den aktuellen Zustand des Systems in Form von Daten (zum Beispiel die Position und den Radius eines Kreises).

Ein (oder auch mehrere Views) visualisieren Daten aus dem Model. So wird beispielsweise der zuvor genannte Kreis in einem durch den Bildschirm vorgegebenen Koordinatensystem an die im Model gespeicherte Position mit dem passenden Radius gezeichnet. Die View agieren als Observer. Das heißt, sie lauschen auf Änderungen am Model und und aktualisieren sich gegebenenfalls.

Der Benutzer (User) nimmt die Visualisierungen wahr (auch als Observer). Er kann überdies mittels so genannten Controllern mit der Anwendung interagieren. Die Controller reagieren auf Devices wie Maus und Tastatur. Der Benutzer kann mit Hilfe derartiger Devices auch Formulare, die in der View enthalten sind, ausfüllen. Änderungen, die der Benutzer (mit Hilfe von geeigneten Devices) an einer View vornimmt, führen zu Änderungen im Model. Das heißt, die meisten Benutzeraktionen haben schlussendlich Änderungen im Model zur Folge, die dann wieder von der View angezeigt werden.

Es gibt noch eine spezielle Kategorie von Controller-Devices: Force Feedback Controller. Diese reagieren z. B. mittels Vibration auf bestimmte Modelländerungen. Force Feedback Controller sind daher in der Regel bidirektional mit dem Model verknüpft.

Key Controller

Man könnte die schon bekannte Funktion sayHelloOnEnter im Konstantenbereich der HelloWorld-Komponente definieren und in den Funktionen onMounted sowie on onUnmounted aktivieren bzw. deaktivieren.

function sayHelloOnEnter(p_event)
{ if (    p_event.key       === 'Enter'
       && p_event.target.id === 'input_name'
     )
  { // Prevent other handlers from handling the keypress event.
    p_event.preventDefault();
    p_event.stopPropagation();
    sayHello();
  }
}

onMounted  (() => window.addEventListener   ('keydown', sayHelloOnEnter))
onUnmounted(() => window.removeEventListener('keydown', sayHelloOnEnter))

Besser ist es allerdings, ein Modul zu definieren, das diese Funktionalität zur Verfügung stellt. Damit trennt man den Controller sehr schön vom Model. Deshalb ist im Sinne von Bertrand Meyer, diese Version zu bevorzugen.

Die Kommentare werden später zur automatischen Generierung einer Dokumentation mittels JSDoc benötigt.

<!-- @/controller/controllerKey -->

/**
  * When the key <code>p_key</code> is pressed while the focus is
  * on the HTML element with the id <code>p_target_id</code>,
  * the callback function is invoked.
  *
  * @param { string } p_key
  *        The name of a keyboard key
  * @param { string } p_target_id
  *        The id of the DOM element
  * @param { function } p_callback
  *        A callback function
  */
function controllerKey(p_key, p_target_id, p_callback)
{  return (p_event) =>
          { if (    p_event.key       === p_key
                 && p_event.target.id === p_target_id
               )
            { // Prevent other handlers from handling the keypress event.
              p_event.preventDefault();
              p_event.stopPropagation();
              p_callback();
            }
          }
}

export default controllerKey

Diese Funktion kann nun in das Modul HelloWorld.vue importiert und dort aktiviert werden.

<script setup>
  ...
  import controllerKey  from '@/controller/controllerKey'

  const
    ...
    sayHelloOnEnter = controllerKey('Enter', inputId, sayHello)

  onMounted
  ( () =>
    { window.addEventListener('keydown', sayHelloOnEnter);
      ...
    }
  )
  onUnmounted
  ( () =>
    { window.removeEventListener('keydown', sayHelloOnEnter);
      ...
    }
  )

Jetzt sollte die Anwendung auch per Enter-Taste korrekt bedient werden können.

ControllerKey: Klasse zum Agieren mit Keyboardevents

Alternativ kann man eine wiederverwendbare Controllerklasse definieren.

Die Aufgabe von Objekten dieser Klasse ist es, auf bestimmte Keyboard-Aktionen des Benutzers hin, sofern sich der HTML-Fokus auf bestimmten HTML-Fomularfeldern befindet, eine Callback-Funktion aufzurufen. Im Gegensatz zur obigen Funktion kann man mehrere Keys und mehrereDOM-IIDs angeben, für die bei Betätigung einer dieser Tasten die Callback-Funktion ausgeführt werden soll.

Außerdem stellt die Klasse zwei Methoden add und remove bereit, mit denen ein zugehöriges Controller-Objekt aktiviert bzw. deaktiviert werden kann.

Hier ist die Frage, ob die zusätzlich Funktionalität wirklich benötigt wird. Falls nein, wäre evtl. eine einfachere Klasse sinnvoll, die nicht mehr Funktionalität als die obige Funktion biete, dafür aber kleiner ist und daher weniger Bandbreite beim Laden durch den Browser verbraucht.

Beachten Sie, dass die Funktion ControllerKey und die Klasse ControllerKeys heißt. Eigentlich würde ich die Namens-Konvention bevorzugen, sie gleich zu benennen, und nur durch den Anfangsbuchstaben Funktion und Klasse zu unterscheiden. Ein kleines „c“ steht für ein Funktion, ein großes „C“ für eine Klasse. Aber leider unterscheidet Windows diese beiden Dateinamen nicht. Daher habe ich an ControllerKey hinten ein s angefügt: ControllerKeys. Da symbolisiert, dass ein Objekt dieser Klasse mehrere Tasten (Keys) auf einmal überwachen kann.

/**
 * @class ControllerKey
 */
class ControllerKey
{ #v_key_events;
  #f_callback;

  /**
    * @param { string | Array<string> } p_keys
    *        The name of a keyboard key or an array of such names
    * @param { string | Array<string> | null } p_targets
    *        The id of an HTML element or an array of such ids.
    * @param { function } p_callback
    *        (p_key?: string, p_target?: string) => void<br>
    *        A callback function that is called when focus is
    *        on an HTML element with a matching id and if a key event
    *        occurs for some of the stated keys.
    * @param { 'keydown' | 'keyup' | 'keypress' } p_key_events
    */
  constructor(p_keys,p_targets,  p_callback, p_key_events = 'keydown')
  { this.#v_key_events = typeof p_key_events === 'string'
                         ? [p_key_events]
                         : p_key_events;

    const
      c_keys    = typeof p_keys    === 'string' ? [p_keys]    : p_keys,
      c_targets = typeof p_targets === 'string' ? [p_targets] : p_targets

    this.#f_callback =
    p_event =>
    { const
        c_key    = p_event.key,
        c_target = p_event.target.id

      if (c_keys.includes(c_key) && (!c_targets || c_targets.includes(c_target)))
      { // Prevent other handlers from handling the keypress event.
        p_event.preventDefault();
        p_event.stopPropagation();
        // Invoke the callback function
        p_callback(c_key, c_target);
      }
    }
  }

  /** Adds the key handler described by this object as keyboard event handler. */
  add()
  { for (const l_key_event of this.#v_key_events)
    { window.addEventListener(l_key_event, this.#f_callback) }
  }

  /** Removes the key handler described by this object as keyboard event handler. */
  remove()
  { for (const l_key_event of this.#v_key_events)
    { window.removeEventListener(l_key_event, this.#f_callback) }
  }
}

export default ControllerKey

Key-Controller einbinden

Die zuvor definierte Klasse kann nun verwendet werden. Dazu wird sie in HelloWorld.vue importiert.

Dort wird ein neues ControllerKey-Objekt definiert. Dieses wird aktiviert, wenn die Komponenten in eine View eingebunden wird (mounted) wird und deaktiviert, wenn sie wieder entfernt wird (unmounted).

<script setup>
  ...
  import ControllerKey from '@/controller/ControllerKeys'

  const
    ...,
    sayHelloOnEnter = new ControllerKey('Enter', inputId, sayHello)

  onMounted
  ( () => 
    { sayHelloOnEnter.add();
      window.addEventListener('visibilitychange', autofocus);
      autofocus();
    }
  )
  onUnmounted
  ( () => 
    { sayHelloOnEnter.remove();
      window.removeEventListener('visibilitychange', autofocus);
    }
  )
</script>

JSDoc

Wenn wir schon dabei sind, können wir auch gleich noch die JSDoc aktivieren. JSDoc generiert aus gemäß dem JSDoc-Standard kommentierten ECMAScript-/Typescript-Dateien automatisch eine HTML-Dokumentation.

Installieren Sie zunächst JSDOc:

  npm i -D jsdoc
  npm i -D docdash  // oder ein anderes Stylepaket Ihrer Wahl

und erstellen Sie eine zugehörige Konfigurationsdatei jsdoc.config.json:

{ "opts": 
  { "encoding":    "utf8",           
    "destination": "./doc/",         
    "recurse":     true,
    "template":    "node_modules/docdash"
  }
}

Fügen Sie dann den nachfolgenden Befehl in das Objekt scripts der Datei package.json ein:

  "jsdoc": "jsdoc -c jsdoc.config.json src"

Wenn Sie nun npm run jsdoc ins Terminal tippen, wird die zugehörige Dokumentation im Ordner doc erzeugt. Diese enthält nur eine Dokumentation für die Dateien controllerKey.js und ControllerKeys.js, da dies die einzigen Dateien ist, die JSDoc-Kommentare enthalten.

Fortsetzung des Tutoriums

Sie sollten nun Teil 3 des Vue-Tutoriums bearbeiten. Die Daten werden in einem Store (Model) gespeichert.

Quellen

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