8. Elm - Eine funktionale Programmiersprache zur Entwicklung von Web-Apps
8.3 Benutzerinput
Als einfachstes Beispiel sehen Sie hier
Website03Counter.elm;
diese Beispiel ist von
elmprogramming.com.
Der Zustand Ihrer Webseite ist ein Objekt vom Typ Model
. Dies
ist ein Typ, den Sie selbst in Ihrem Code definieren. Da wir hier nur ein
Integer speichern, ist Model
nur ein Alias für Int
,
nämlich per type alias Model = Int
. Im Allgemeinen wird
Model
viel komplexere Daten enthalten.
Das Verhalten Ihrer Webseite wird von den folgenden Funktionen festgelegt:
- der Funktion
view : Model -> Html Message
, die beschreibt, wie aus den Daten des Modells die Html-Seite geschaffen werden soll; - der Funktion
update : Message -> Model -> Model
, die beschreibt, wie eine hereinkommende Nachricht das Modell verändern soll; - der Konstante
init : Model
, die den Wert festlegt, den das Modell am Anfang, also beim Laden der Seite haben soll.
Manche Html-Elemente müssen wissen, dass Sie unter Umständen eine Nachricht schicken sollen. Zum Beispiel
button [ onClick Increment ] [ text "+" ]
Dadurch wird festgelegt, dass auf Knopfdruck die Nachricht Increment
geschickt
werden soll. Increment
ist ein von uns selbst definierter Wert vom Typ
Message
. Daher muss Html
ein parametrisierter Typ mit
Typ-Variable msg
sein. Genau wie es List a
heißt,
weil wir im Allgemeinen nicht wissen, welche Typen unsere Liste speichern soll und
daher die Typenvariable a
als Platzhalter verwenden,
so weiß der Typ Html
ja nicht, welcher Typ von Nachrichten verschickt wird,
und deswegen muss es Html msg
heißen.
Ein etwas komplexeres Beispiel finden Sie unter Website03ExpandList.elm.
Übungsaufgabe
Passen Sie Website03ExpandList.elm an,
so dass es nicht ein <ul>
-Element erschafft, sondern die
Werte in einer Tabelle mit zwei Spalten anzeigt. Die linke Spalte soll
die Indizes \(n\) enthalten, die rechte die entsprechenden Fibonacci-Zahlen
\(F_n\).
Offener Input: Textfelder etc.
In der Anwendung Website04EchoString.elm sehen Sie eine Seite, die den eingegebenen String in Kleinbuchstaben umwandelt. Die Codezeile
input [ onInput ValueChanged, value model.input ] []
erschafft ein HTML-Input-Element, das jedes Mal, wenn sein Wert sich ändert, die Message
ValueChanged
an unseren Code schickt. Allerdings nützt uns die Nachricht allein
nichts. Wie müssen ja auch den neuen Wert wissen; die Nachricht braucht also eine
Payload.
Sehen Sie, Sie können in Elm aus fundamentalen Gründen nichts wie
inputField.getValue()
schreiben; das wäre nicht funktional: die Funktion würde,
abhängig
vom Kontext, andere Werte zurückliefern. Wie wird dieses Problem in Elm gelöst?
Erforschen wir, worum es sich bei onInput
denn handelt:
Also:elm repl
import Html.Events
Html.Events.onInput
<function> : (String -> msg) -> Html.Attribute msg
onInput
will als Argument keine Message
, sondern eine
Funktion, die aus einem String
eine Message
baut. Wie
zum Beispiel unseren Datenkonstruktur ValueChanged
:
Nochmal: obwohlimport Website04EchoString
Website04EchoString.ValueChanged
<function> : String -> Website04EchoString.Message
ValueChanged
ist zwar streng gesehen keine Funktion, da sie
nichts "ausführt"; es ist ein Datenkonstruktur, der sich aber syntaktisch und semantisch wie
eine
Funktion verhält. Der Ausdruck ValueChanged "Auf Wiedersehen"
macht aus dem String
"Auf Wiedersehen"
die Message
ValueChanged "Auf Wiedersehen"
. Das Ergebnis des "Funktionsaufrufes" ist also
der Aufruf selbst. Gewöhnungsbedürftig, aber elegant.
Die Semantik der Zeile input [ onInput ValueChanged, value model.input ] []
ist nun, dass, wenn sich der Inhalt des Textfeldes verändert (z.B. durch User-Input) und
x
der neue Inhalt ist, dann
schickt die Laufzeitumgebung an unseren Elm-Code die Nachricht ValueChanged x
.
In
der Funktion update
fangen wir den Wert x
in den Zeilen
case message of
ValueChanged newString ->
{ model | input = newString }
auf und können ihn weiterverarbeiten.
Übungsaufgabe Schreiben Sie eine einfache Webseite, wo der User eine Zahl in ein Eingabefeld eingeben kann; die entsprechende Fibonacci-Zahl wird dann ausgerechnet und angezeigt.
Hinweis. Die zusätzliche Herausforderung liegt jetzt darin,
den Eingabe-String in ein Int
zu verwandeln.
Dies geht mit der Funktion String.toInt
. Was geschieht, wenn der
eingegebe String kein Integer darstellt?