A package for viewing data from a List
. Features currently include sorting and pagination.
A key characteristic of this package is that it tries to seperate transforming the data (i.e., sort, paginate, group, filter, etc.) from actually viewing it (e.g., rendering a HTML table, CSS grid, etc.).
This module, ListView
, is only responsible for the first part, however the ListView.Viewers
module provides
some standard viewers that should be sufficient for many use cases.
Here's a small example of how to render a list of data as a HTML table, using the viewer ListView.Viewers.viewAsHtmlTable
.
Note that we also use the TableMsg
type and updateTable
function from that same module, which are needed by
the viewHtmlTable
function. As is ususal with other Elm packages, the updateTable
function needs to be
called from your main update
function somehow.
-- main entity type we want to view
type alias Character =
{ fullName : String
, power : Int
, imageUrl : String
}
-- some data, this is the list we want to view
rows : List Character
rows = [...]
-- this is just a helper function to render an image
viewPicture : character -> Html msg
viewPicture row =
Html.img [ class_ "avatar", src row.imageUrl ] []
-- config for my list to be viewed as an HTML table with 3 columns
tableConfig : Config Character Msg
tableConfig =
ListView.makeConfig
|> ListView.withColumn (ListView.makeColumn.html "" viewPicture)
|> ListView.withColumn (ListView.makeColumn.string "Name" .fullName)
|> ListView.withColumn (ListView.makeColumn.int "Power" .power)
-- we need to store the `DataList` state in our model
type alias Model = { tableState : ListView.State }
-- our app needs to be able to process messages from the `DataListViewer` (e.g., sort by this column)
type Msg
= OnTableMsg ListView.Viewers.ListViewMsg
-- our main `update` function needs to handle messages from the table
update : Msg -> Model -> Model
update msg model =
case msg of
OnTableMsg tableMsg ->
{ model | tableState = ListView.Viewers.update rows tableMsg model.tableState }
view : Model -> Html Msg
view model =
ListView.Viewers.viewAsHtmlTable OnTableMsg tableConfig model.tableState rows
main : Program () Model Msg
main =
Browser.sandbox
{ init = { tableState: ListView.makeState }
, view = view
, update = update
}
Opaque type holding all the static configuration of the list (basically, a list with all the viewable columns and their behavior)
makeConfig : Config a msg
Make an empty (i.e., without columns) table configuration. This is the starting point for adding new columns
makeConfig
|> withColumn (makeColumn.html "" viewPicture)
|> withColumn (makeColumn.string "Name" .fullName)
|> withColumn (makeColumn.string "Tagline" .tagLine)
|> withColumn (makeColumn.int "Power" .power)
makeColumn : { string : String -> (a -> String) -> ColumnConfig a msg, int : String -> (a -> Basics.Int) -> ColumnConfig a msg, float : String -> (a -> Basics.Float) -> ColumnConfig a msg, html : String -> (a -> RowIndex -> Html msg) -> ColumnConfig a msg }
Namespace for aggregating all the functions that create new columns for the data list. Some examples:
makeColumn.string "Car" .carName
.carName
must return a String
value. Column is sortable alphabetically by default.
makeColumn.int "Age" .age
makeColumn.float "Salary" .salary
Values are converted to String
for rendering, but are sorted numerically by default.
makeColumn.html "Company url" (\row rowIndex -> Html.a [href row.url] [Html.text row.name])
The accessor function must return Html msg
. Can be used to render just about anything. These columns are not sortable by default.
An important difference from the other column types is that the accessor function has access to the index of the row
in the original list. This is sometimes usefule if the generated Html
wants to produce messages that need to identify
the row by its index (e.g., a message to update the row somehow).
Note: This namespace only exists because there are already too many exposed functions in this module, and I wanted to group related functions into a common namespace, without creating a separate module.
Not really sure this is a good idea, may change in the future.
withColumn : ColumnConfig a msg -> Config a msg -> Config a msg
Add a new column to a table configuration. Columns can be created with makeColumn.XXX
functions
withColumns : List (ColumnConfig a msg) -> Config a msg -> Config a msg
Add a bunch of new columns to a table configuration. Columns can be created with makeColumn.XXX
functions
Holds all the state of the ListView
, like how it's currently being sorted or what the current page is.
You definately want to store this in your Model
. In the future this should be exportable/importable in order to
be stored in local storage, for example.
makeState : State
Creates a State
with default setting: page size of 10 elements and no sorting.
Sometimes the output of the standard viewers defined in ListView.Viewers
may not be suitable for
your needs; maybe you want to layout your page using elm-ui instead; or maybe you just need a
simpler paginator with only Previous and Next buttons.
In those cases, you may want to skip the ListView.Viewers
altogether, and use the ListView.getViewInfo
instead, which returns all the information needed (hopefully) to render the list, already sorted, filtered, etc. This is
exactly what the viewers from the ListView.Viewers
module do, so be sure to check the source code.
One caveat is that, in order for external packages to be able to render data from the ListView
module, a lot
of (otherwise internal) details had to be exposed. This creates a very large public API, which will mostly likely change
when this package evolves, creating many breaking changes along the way. More so in codebases that define their
own viewers. Keep this in mind when going down this route!
A final note: many customizations can be made to the standard viewers using CSS alone. You may not need build your own viewer!
getViewInfo : Config a msg -> State -> List a -> ListViewInfo a msg
Takes in all the data, configuration and state and produces all the
information needed to view the ListView
, poperly sorted, paginated and formatted.
Advanced usages can call this function to completely customize what gets rendered, and what messages are produced.
Typical usages that just need an HTML table or a CSS grid/flexbox should just use the
corresponding viewer from ListView.Viewers
(which all call this function under the hoods).
updateState : { withPage : Basics.Int -> PageChange -> State -> State, withSorting : ListViewSortState -> State -> State, withRowsPerPage : Basics.Int -> State -> State }
Namespace for aggregating all the functions that update the given ListView.State
.
updateColumn : { withName : String -> ColumnConfig a msg -> ColumnConfig a msg, withCode : String -> ColumnConfig a msg -> ColumnConfig a msg, withSorter : (a -> comparable) -> ColumnConfig a msg -> ColumnConfig a msg, withViewer : (a -> RowIndex -> ColumnOutput msg) -> ColumnConfig a msg -> ColumnConfig a msg }
Namespace for aggregating all the functions that change the configuration
an existing Column
(i.e., how it's displayed or sorted, or even its name).
For example, to create a sortable HTML column:
showUsername person =
Html.span []
[ Html.img [ src person.avatar ] []
, Html.text person.name
]
makeColumn.html "Name" showUsername .name
Direction of sorting. Note that currently, if a column is sortable, it can always be sorted in both directions.
Information on how the table is currently being sorted. Part of State
Represents a change in the current page
{ columnsForRow : a -> RowIndex -> List (Html msg)
, rowsToDisplay : List ( RowIndex
, a )
, columnsViewInfo : List ColumnViewInfo
, pagingViewInfo : PagingViewInfo
}
Type that holds all the necessary information for rendering a ListView
{ numberOfRows : Basics.Int
, numberOfPages : Basics.Int
, rowsPerPage : Basics.Int
, currentPage : Basics.Int
, currentPageStartIndex : Basics.Int
, currentPageEndIndex : Basics.Int
}
Information about the ListView
paging, should contain all that's needed to render a paginator
{ name : String
, code : String
, index : ColumnIndex
, sortInfo : ColumnSortInfo
}
Type that holds all the necessary information for rendering a column header
Represents sort state for a single column