Elm Calculator Part 8 - Support Keypad Input
Tags: elm, webdev, beginners, codenewbie
Published: 2020-05-02 22:15:47

---
title: Elm Calculator Part 8 - Support Keypad Input
published: true
date: 2020-05-01 00:00:00 UTC
tags: elm, webdev, beginners, codenewbie 
canonical_url: https://pianomanfrazier.com/post/elm-calculator-book/08-keypad-input/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/u26mai0h3j5xcrz6tbis.png
series: Elm Calculator
---

_This post is part of a series. To see all published posts in the series see the tag ["elm calculator book"](https://pianomanfrazier.com/tags/elm-calculator-book/).If you wish to support my work you can purchase the book on [gumroad](https://gum.co/NwMtt)._

**This project uses Elm version 0.19**

- Part 1 - Introduction 
- Part 2 - Project Setup 
- Part 3 - Add CSS 
- Part 4 - Basic Operations 
- Part 5 - Adding Decimal Support 
- Part 6 - Supporting Negative Numbers 
- Part 7 - Add Dirty State 
- **_Part 8 - Support Keypad Input (this post)_**
- Part 9 - Combination Key Input 
- Part 10 - Testing 
- Part 11 - Netlify Deployment 

* * *

- **_browse:_** [https://gitlab.com/pianomanfrazier/elm-calculator/-/tree/v0.8](https://gitlab.com/pianomanfrazier/elm-calculator/-/tree/v0.8)
- **_diff:_** [https://gitlab.com/pianomanfrazier/elm-calculator/-/compare/v0.7...v0.8](https://gitlab.com/pianomanfrazier/elm-calculator/-/compare/v0.7...v0.8)
- **_ellie:_** [https://ellie-app.com/72nXvQqPq37a1](https://ellie-app.com/72nXvQqPq37a1)

It would be nice if users could input numbers into our calculator using their number pad on their keyboard.

We need to tell our program what to do when a user presses these keys. So we need to add some more messages to our application.

We will be using the package [Gizra/elm-keyboard-event](https://package.elm-lang.org/packages/Gizra/elm-keyboard-event/latest/).

First we need to install the package so we can import it into our code. The ellie-app version of this chapter has the package installed. Open the side menu and look at the installed packages. You should see I have added 3 new packages as dependencies.

![Ellie app installed packages](https://pianomanfrazier.com/img/elm-calculator/ellie-app-packages.png)

To install a package in ellie-app use the search bar.

![Install package on Ellie app](https://pianomanfrazier.com/img/elm-calculator/ellie-install-package.png)

Or you can install a package locally on the command line.

```
elm install Gizra/elm-keyboard-event
```

We will also need to import [SwiftsNamesake/proper-keyboard](https://package.elm-lang.org/packages/SwiftsNamesake/proper-keyboard/latest/) which is a dependency of elm-keyboard-event. We need this because we will be using these types directly in our application.

```
elm install SwiftsNamesake/proper-keyboard
```

And lastly we need `elm/json` to decode the JSON message coming from the browser for our events. Don't worry. This is not as scary as it sounds.

```
elm install elm/json
```

## Refactor to use Elm subscriptions

Since these keyboard events are going to be coming from the browser, our Elm application needs to subscribe to these events. In order to do subscriptions we need to refactor our application.

Right now we have been using `Browser.sandbox` in our main function.

```elm
main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }
```

### Change to `Browser.element`

We need to change it to `Browser.element` so that we can add subscriptions.

```elm
main : Program () Model Msg
main =
    Browser.element
        { view = view
        , init = \_ -> init
        , update = update
        , subscriptions = subscriptions
        }
```

This is going to impact our whole application. We'll let the compiler guide us through this refactor. The biggest change will be to our `update` function.

It will need to change from

```elm
update : Msg -> Model -> Model
```

to

```elm
update Msg -> Model -> ( Model, Cmd Msg )
```

We need to return the model and a command message. We won't be using command messages in this application so don't worry about it. We just need to fix the code so it will compile again.

Most of the time we can just return the tuple `( model, Cmd.none )`.

```elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        SetDecimal ->
            if String.contains "." model.currentNum then
                ( model, Cmd.none )
            ...
```

The `init` also needs to return a command message.

```elm
init : ( Model, Cmd Msg )
init =
    ( { stack = []
        , ...
        }
    , Cmd.none
    )
```

## Add subscriptions

Now we can work on adding the subscriptions. First we need to import some stuff.

```elm
import Browser.Events exposing (onKeyDown)
import Json.Decode as D
import Keyboard.Event as KE exposing (KeyboardEvent, decodeKeyboardEvent)
import Keyboard.Key as KK
```

We are now going to subscribe to the `onKeyDown` event in the browser. After our application gets the event we then need to decode the event into something Elm can deal with. Since events come in the from the browser as a JSON message we need to decode the JSON into an Elm type.

Luckily for us, we don't need to worry about writing a decoder for these events. The `Gizra/elm-keyboard` package provides this for us.

```elm
subscriptions : Model -> Sub Msg
subscriptions model =
    onKeyDown (D.map HandleKeyboardEvent decodeKeyboardEvent)
```

This `subscriptions` function is going to return a subscription message. In this case a `HandleKeyboardEvent` type.

We need that in our message type.

```elm
type Msg
    = ...
    | HandleKeyboardEvent KeyboardEvent
```

## Handle the key events

Now we can put this message and handle it in our update function.

```elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ...
        HandleKeyboardEvent event ->
            case event.keyCode of
                KK.Add ->
                    update (InputOperator Add) model
                KK.Subtract ->
                    update (InputOperator Sub) model
                KK.NumpadZero ->
                    update (InputNumber 0) model
                ...
                -- ignore anything else
                _ -> ( model, Cmd.none )
```

I used a small trick here. When we get a key matching one of our cases, just call the appropriate update function again.

What I hope you take away from this chapter is how nice it is to refactor things in Elm. We made some sweeping changes to our application and the compiler was able to help us out.

Now that we have keyboard input, it would be nice if the user had some more options for deleting the stack frames. The next chapter will cover using key combinations such as ctrl-shift-delete to clear out the frames.