@@ -15,7 +15,11 @@ defmodule Corex.DataTable.Sort do
1515 socket =
1616 socket
1717 |> assign(:users, fetch_users())
18- |> Corex.DataTable.Sort.assign_for_sort(:users, default_sort_by: :id, default_sort_order: :asc)
18+ |> Corex.DataTable.Sort.assign_for_sort(:users,
19+ default_sort_by: :id,
20+ default_sort_order: :asc,
21+ sort_columns: [:id, :name]
22+ )
1923
2024 {:ok, socket}
2125 end
@@ -47,19 +51,24 @@ defmodule Corex.DataTable.Sort do
4751
4852 - `:default_sort_by` – atom; column `name` on [`data_table/1`](Corex.DataTable.html#data_table/1) (e.g. `:id`)
4953 - `:default_sort_order` – `:asc` or `:desc`, default `:asc`
54+ - `:sort_columns` – list of atoms the client may sort by (e.g. `[:id, :name]`). When set,
55+ [`handle_sort/3`](#handle_sort/3) ignores unknown or disallowed `"sort_by"` values instead of
56+ raising. Always set this in production LiveViews.
5057
5158 The socket must already have an assign at `rows_assign` (e.g. `:users`) with the same list
5259 passed as `rows` to [`data_table/1`](Corex.DataTable.html#data_table/1). Adds `:sort_by`,
53- `:sort_order`, and replaces the rows assign with the sorted list.
60+ `:sort_order`, `:sort_columns`, and replaces the rows assign with the sorted list.
5461 """
5562 def assign_for_sort ( socket , rows_assign , opts \\ [ ] ) do
5663 sort_by = Keyword . get ( opts , :default_sort_by )
5764 sort_order = Keyword . get ( opts , :default_sort_order , :asc )
65+ sort_columns = Keyword . get ( opts , :sort_columns )
5866 rows = socket . assigns [ rows_assign ] || [ ]
5967
6068 socket
6169 |> assign ( :sort_by , sort_by )
6270 |> assign ( :sort_order , sort_order )
71+ |> assign ( :sort_columns , sort_columns )
6372 |> assign ( rows_assign , sort_rows ( rows , sort_by , sort_order ) )
6473 end
6574
@@ -69,11 +78,19 @@ defmodule Corex.DataTable.Sort do
6978 Use in `handle_event("sort", params, socket)` (same name as `on_sort`) and return
7079 `{:noreply, Corex.DataTable.Sort.handle_sort(socket, params, :users)}`.
7180
72- `params` must contain `"sort_by"` (string, e.g. `"id"`). It is converted to an atom.
81+ `params` must contain `"sort_by"` (string, e.g. `"id"`). When `:sort_columns` was set via
82+ [`assign_for_sort/3`](#assign_for_sort/3), only those columns are accepted; other values are
83+ ignored and the socket is returned unchanged. Unknown atoms never crash the LiveView process.
7384 `rows_assign` is the assign key passed to [`data_table/1`](Corex.DataTable.html#data_table/1) as `rows`.
7485 """
7586 def handle_sort ( socket , % { "sort_by" => sort_by_param } , rows_assign ) do
76- sort_by = String . to_existing_atom ( sort_by_param )
87+ case parse_sort_by ( sort_by_param , socket . assigns [ :sort_columns ] ) do
88+ { :ok , sort_by } -> apply_sort ( socket , sort_by , rows_assign )
89+ :error -> socket
90+ end
91+ end
92+
93+ defp apply_sort ( socket , sort_by , rows_assign ) do
7794 current_sort_by = socket . assigns . sort_by
7895 current_sort_order = socket . assigns . sort_order
7996
@@ -92,6 +109,22 @@ defmodule Corex.DataTable.Sort do
92109 |> assign ( rows_assign , sort_rows ( rows , sort_by , sort_order ) )
93110 end
94111
112+ defp parse_sort_by ( param , columns ) when is_list ( columns ) do
113+ with { :ok , sort_by } <- safe_existing_atom ( param ) , true <- sort_by in columns do
114+ { :ok , sort_by }
115+ else
116+ _ -> :error
117+ end
118+ end
119+
120+ defp parse_sort_by ( param , _columns ) , do: safe_existing_atom ( param )
121+
122+ defp safe_existing_atom ( param ) when is_binary ( param ) do
123+ { :ok , String . to_existing_atom ( param ) }
124+ rescue
125+ ArgumentError -> :error
126+ end
127+
95128 defp toggle_sort_order ( :asc ) , do: :desc
96129 defp toggle_sort_order ( :desc ) , do: :asc
97130
0 commit comments