FabienHenon / elm-infinite-scroll / InfiniteScroll

Infinite scroll allows you to load more content for the user as they scroll (up or down).

The infinite scroll must be bound to an Html element and will execute your own Cmd when the user scrolled to the bottom (or top) of the element.

The Cmd can be anything you want from local data fetching or complex requests on remote APIs. All it has to do is to return a Cmd msg and call stopLoading once fetching is finished so that the infinite scroll can continue asking for more content.

Definitions


type alias LoadMoreCmd msg =
Direction -> Platform.Cmd.Cmd msg

Definition of the function you must provide to the API. This function will be called as soon as new content is required

loadMore : InfiniteScroll.Direction -> Cmd Msg
loadMore dir =
    Task.perform OnLoadMore <| Task.succeed dir

InfiniteScroll.init loadMore


type Direction
    = Top
    | Bottom

Scroll direction.

Initialization

init : LoadMoreCmd msg -> Model msg

Creates a new Model. This function needs a LoadMoreCmd that will be called when new data is required.

type Msg
    = OnLoadMore InfiniteScroll.Direction

type alias Model =
    { infiniteScroll : InfiniteScroll.Model Msg }

loadMore : InfiniteScroll.Direction -> Cmd Msg
loadMore dir =
    Task.perform OnLoadMore <| Task.succeed dir

initModel : Model
initModel =
    { infiniteScroll = InfiniteScroll.init loadMore }

timeout : Basics.Float -> Model msg -> Model msg

Sets a different timeout value (default is 5 seconds)

When timeout is exceeded stopLoading will be automatically called so that infinite scroll can continue asking more content event when previous request did not finished.

init loadMore
    |> timeout (10 * 1000)

offset : Basics.Int -> Model msg -> Model msg

Sets a different offset (default 50).

Offset is the number of pixels from top or bottom (depending on Direction value) from which infinite scroll will detect it needs more content.

For instance with offset set to 50 and direction to Top. Once scroll position is 50 pixels or less from the top of the element it will require new content. The same applies with a direction set to Bottom except it will check for the distance with the bottom of the element.

init loadMore
    |> offset 100

direction : Direction -> Model msg -> Model msg

Sets a different direction (default to Bottom).

A direction set to Bottom will check distance of the scroll bar from the bottom of the element, whereas a direction set to Top will check distance of the scroll bar from the top of the element.

init loadMore
    |> direction Top

loadMoreCmd : LoadMoreCmd msg -> Model msg -> Model msg

Sets a different command to load content.

It is useful if you need to change your request between two commands. You probably use it when your request is received.

newRequest page =
    Task.perform OnLoadMore <| Task.succeed page

{ model | infiniteScroll =
    model.infiniteScroll
        |> loadMoreCmd (newRequest model.page)
}

Update

update : (Msg -> msg) -> Msg -> Model msg -> ( Model msg, Platform.Cmd.Cmd msg )

The update function must be called in your own update function. It will return an updated Model and commands to execute.

type Msg
    = InfiniteScrollMsg InfiniteScroll.Msg

type alias Model =
    { infiniteScroll : InfiniteScroll.Model Msg }

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        InfiniteScrollMsg msg_ ->
            let
                ( infiniteScroll, cmd ) =
                    InfiniteScroll.update InfiniteScrollMsg msg_ model.infiniteScroll
            in
            ( { model | infiniteScroll = infiniteScroll }, cmd )

Scroll

infiniteScroll : (Msg -> msg) -> Html.Attribute msg

Function used to bind the infinite scroll on an element.

The element's height must be explicitly set, otherwise scroll event won't be triggered

type Msg
    = InfiniteScrollMsg InfiniteScroll.Msg

view : Model -> Html Msg
view _ =
    let
        styles =
            [ ( "height", "300px" ) ]
    in
        div [ infiniteScroll InfiniteScrollMsg, Attributes.style styles ]
            [ -- Here will be my long list -- ]

stopLoading : Model msg -> Model msg

Stops loading. You should call this function when you have finished fetching new data. This tells infinite scroll that it can continue asking you more content.

If you forget to call this function or if your data fetching is too long, you will be asked to retrieve more content after timeout has expired.

startLoading : Model msg -> Model msg

Starts loading more data. You should never have to use this function has it is automatically called when new content is required and your loadMore command is executed.

isLoading : Model msg -> Basics.Bool

Checks if the infinite scroll is currently in a loading state.

Which means it won't ask for more data even if the user scrolls

Advanced

cmdFromScrollEvent : (Msg -> msg) -> Json.Decode.Value -> Platform.Cmd.Cmd msg

Only use this function if you handle on "scroll" event yourself (for instance if another package is also using the scroll event on the same node)

If you wish to use the document scroll, you should create a port to catch the document scroll and send it to this function via the subscriptions

The function returns a Cmd msg that will perform the model update normally done with infiniteScroll. You have to pass it a Json.Decode.Value directly coming from on "scroll" event

type Msg
    = InfiniteScrollMsg InfiniteScroll.Msg
    | OnScroll JsonDecoder.Value

view : Model -> Html Msg
view model =
    div [ on "scroll" (JsonDecoder.map OnScroll JsonDecoder.value) ] [ -- content -- ]

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        -- ... --

        InfiniteScrollMsg msg_ ->
            let
                ( infScroll, cmd ) =
                    InfiniteScroll.update InfiniteScrollMsg msg_ model.infScroll
            in
                ( { model | infScroll = infScroll }, cmd )

        OnScroll value ->
            ( model, InfiniteScroll.cmdFromScrollEvent InfiniteScrollMsg value )

onScrollUpdate : (Msg -> msg) -> Json.Decode.Value -> Model msg -> ( Model msg, Platform.Cmd.Cmd msg )

Like cmdFromScrollEvent except it returns the updated Model and the Cmd to send instead of only a simple Cmd that will be executed after

Types


type Model msg

Model of the infinite scroll module. You need to create a new one using init function.


type Msg

Infinite scroll messages you have to give to the update function.