athanclark/elm-debouncer/1.0.0

NOTE: this package is not compatible with Elm 0.19.1

Elm-Debouncer

HEED ALL YEE WHO HATH WANT TO DEBOUNCE STUFF, because it's chill and we all know what happens to chill people >_>

Basically the idea is simple, a Debouncer is a stateful component that manages when an arbitrary action of your own should be issued. The Deboucer itself is basically the model for the delay, so you should have a separate one for different isolated miniscule components of your applicaiton - like one for each text box.

To let the debouncer actually issue your actions for you, you need to delegate that work to the updating function - updateDebouncer. Note that it needs the delay and action to issue as read-only data - we won't be changing the delay or action to issue dynamically here.

When viewing this system correctly, we should consider the debouncer somewhat like a service - when we issue the Start action / message, we're actually asking the debouncer to issue the main action. from this perspective, DebouncerMsg (with Start as the only exposed constructor) as our input interface - the interface we can solicit with queries, while DebouncerResults is our output interface - representing either more debouncing or an issuance of the main action we wish to debounce. Luckily this output channel is very easy to handle, using handleDebouncerResults all we need is something that manages our inputs, so to say that we understand how to handle a DebouncerMsg means we can handle a DebouncerResults. Sorry :\ I'll explain more in a second.

Okay, so here's an example:

type alias MyModel =
  { somethingDank : Dank
  , myDebouncer   : Debouncer String
  }

-- setting up a debouncer which stagnates String
-- data changes

initMyModel : MyModel
initMyModel =
  { somethingDank = ayyLmao
  , myDebouncer   = initDebouncer
  }
  
type MyAction
  = DankAction DankAction
  | DebouncerMsg (DebouncerMsg String)
  
updateMyModel : MyAction
             -> MyModel
             -> (MyModel, Cmd MyAction)
updateMyModel action model =
  case action of
    DankAction a ->
      let (awwYiss, eff) = updateDank a model.somethingDank
      in  ( { model | somethingDank = awwYiss }
          , Cmd.map DankAction eff
          )
    DebouncerMsg a ->
      let (newDebouncer, eff) = updateDebouncer
                                  (500 * millisecond)
                                  (\s -> DankAction <| SomeDankAction s)
                                  -- this action _needs_ a string
                                  a
                                  model.myDebouncer
      in  ( { model | myDebouncer = newDebouncer }
          , Cmd.map (handleDebouncerResults DebouncerMsg) eff
          ) -- tie up the system
      -- note that `eff : DebouncerResults String MyAction`
          
viewMyModel : MyModel
           -> Html MyAction
viewMyModel model =
  div []
    [ h2 [] [text "Dank, right?"]
    , input [ type' "text"
            , on "input" ( targetValue `Json.Decode.andThen` \s ->
                           Json.Decode.succeed (DebouncerMsg <| Bounce s)
                           -- plumbing the string
                         )
            , value model.somethingDank.dankness
            ] []
    ]