Skip to content

Commit 16071a0

Browse files
authored
Merge pull request #1351 from naymspace/feature/remove-readonly-callback
Remove `render_form_readonly` callback from fields
2 parents cbd1f5f + a55bd15 commit 16071a0

File tree

10 files changed

+87
-194
lines changed

10 files changed

+87
-194
lines changed

guides/fields/readonly.md

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Readonly
22

3-
Fields can be configured to be readonly. In edit view, these fields are rendered with the additional HTML attributes `readonly` and `disabled`, ensuring that the user cannot interact with the field or change its value.
3+
Fields can be configured to be readonly. In edit view, these fields are rendered with the additional HTML attributes `readonly` and `disabled`, ensuring that users cannot interact with the field or change its value.
44

5-
In index view, if readonly and index editable is set to `true`, forms will be rendered with the `readonly` HTML attribute.
5+
In index view, if readonly and index editable are both set to true, forms will be rendered with the `readonly` HTML attribute.
66

77
## Supported fields
88

9-
On index view, read-only is supported for all fields with the index editable option (see [Index Edit](index-edit.md)).
9+
On index view, readonly is supported for all fields with the index editable option (see [Index Edit](index-edit.md)).
1010

11-
On edit view, read-only is supported for:
11+
On edit view, readonly is supported for:
1212
- `Backpex.Fields.Date`
1313
- `Backpex.Fields.DateTime`
1414
- `Backpex.Fields.Number`
@@ -17,19 +17,19 @@ On edit view, read-only is supported for:
1717

1818
## Configuration
1919

20-
To enable read-only for a field, you need to set the `readonly` option to `true` in the field configuration. This key must contain either a boolean value or a function that returns a boolean value.
20+
To enable readonly for a field, you need to set the `readonly` option to true in the field configuration. This key must contain either a boolean value or a function that returns a boolean value.
2121

2222
```elixir
2323
# in your resource configuration file
2424
def fields do
2525
[
26-
rating: %{
27-
module: Backpex.Fields.Text,
28-
label: "Rating",
29-
readonly: fn assigns ->
30-
assigns.current_user.role in [:employee]
31-
end
32-
}
26+
rating: %{
27+
module: Backpex.Fields.Text,
28+
label: "Rating",
29+
readonly: fn assigns ->
30+
assigns.current_user.role in [:employee]
31+
end
32+
}
3333
]
3434
end
3535
```
@@ -38,41 +38,54 @@ end
3838
# in your resource configuration file
3939
def fields do
4040
[
41-
rating: %{
42-
module: Backpex.Fields.Text,
43-
label: "Rating",
44-
readonly: true
45-
}
41+
rating: %{
42+
module: Backpex.Fields.Text,
43+
label: "Rating",
44+
readonly: true
45+
}
4646
]
4747
end
4848
```
4949

5050
## Readonly for custom fields
5151

52-
You can also add readonly functionality to a custom field. To do this, you need to define a [`render_form_readonly/1`](Backpex.Field.html#c:render_form_readonly/1) function. This function must return markup to be used when readonly is enabled.
52+
You can also add readonly functionality to a custom field. To do this, you need to handle the readonly state in the `c:Backpex.Field.render_form/1` function of your custom field. You can access the readonly value from the assigns, which will be `true` or `false`.
5353

5454
```elixir
5555
@impl Backpex.Field
56-
def render_form_readonly(assigns) do
56+
def render_form(assigns) do
5757
~H"""
5858
<div>
59-
<Layout.field_container>
60-
<:label>
61-
<Layout.input_label text={@field[:label]} />
62-
</:label>
63-
<BackpexForm.input
64-
type="text"
65-
field={@form[@name]}
66-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
67-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
68-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
69-
readonly
70-
disabled
71-
/>
72-
</Layout.field_container>
59+
<Layout.field_container>
60+
<:label>
61+
<Layout.input_label text={@field[:label]} />
62+
</:label>
63+
<BackpexForm.input
64+
type="text"
65+
field={@form[@name]}
66+
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
67+
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
68+
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
69+
readonly={@readonly}
70+
disabled={@readonly}
71+
/>
72+
</Layout.field_container>
7373
</div>
7474
"""
7575
end
7676
```
7777

78-
When defining a custom field with index editable support, you need to handle the readonly state in the index editable markup. There is a `readonly` value in the assigns, which will be `true` or `false`.
78+
If your readonly logic is more complex, you can also use a dedicated function that returns the markup for the readonly state.
79+
80+
```elixir
81+
@impl Backpex.Field
82+
def render_form(%{readonly: true} = assigns) do
83+
# Return readonly markup
84+
end
85+
86+
def render_form(%{readonly: false} = assigns) do
87+
# Return editable markup
88+
end
89+
```
90+
91+
When defining a custom field with index editable support, you need to handle the readonly state in `c:Backpex.Field.render_index_form/1`. There is also a readonly value in the assigns, which will be `true` or `false`.

guides/upgrading/v0.14.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,22 @@ def changeset(change, attrs, _metadata) do
8181
|> Ecto.Changeset.validate_required([:field1])
8282
end
8383
```
84+
85+
## `render_form_readonly/1` callback has been removed
86+
87+
We removed the `render_form_readonly/1` callback from fields. Instead, `readonly` must be handled directly in the `c:Backpex.Field.render_form/1` callback.
88+
89+
Make sure to update your custom fields accordingly. The `readonly` value will be available in the assigns, which will be `true` or `false`.
90+
91+
```elixir
92+
@impl Backpex.Field
93+
def render_form(%{readonly: true} = assigns) do
94+
# Render readonly field
95+
end
96+
97+
def render_form(%{readonly: false} = assigns) do
98+
# Render editable field
99+
end
100+
```
101+
102+
See the [Readonly guide](readonly.md) for more details.

lib/backpex/field.ex

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,6 @@ defmodule Backpex.Field do
149149
"""
150150
@callback render_index_form(assigns :: map()) :: %Phoenix.LiveView.Rendered{}
151151

152-
@doc """
153-
Used to render the readonly version of the field.
154-
"""
155-
@callback render_form_readonly(assigns :: map()) :: %Phoenix.LiveView.Rendered{}
156-
157152
@doc """
158153
The field to be displayed on index views. In most cases this is the name / key configured in the corresponding field definition.
159154
In fields with associations this value often differs from the name / key. The function will receive the field definition.
@@ -211,7 +206,7 @@ defmodule Backpex.Field do
211206
) ::
212207
Ecto.Query.dynamic_expr()
213208

214-
@optional_callbacks render_form_readonly: 1, render_index_form: 1
209+
@optional_callbacks render_index_form: 1
215210

216211
@doc """
217212
Returns the default config schema.
@@ -264,11 +259,8 @@ defmodule Backpex.Field do
264259
def render(%{type: :index} = assigns) do
265260
if Backpex.Field.index_editable_enabled?(assigns.field_options, assigns) do
266261
case Map.get(assigns.field_options, :render_index_form) do
267-
nil ->
268-
apply(__MODULE__, :render_index_form, [assigns])
269-
270-
func ->
271-
func.(assigns)
262+
fun when is_function(fun, 1) -> fun.(assigns)
263+
nil -> apply(__MODULE__, :render_index_form, [assigns])
272264
end
273265
else
274266
Map.get(assigns.field_options, :render, &render_value/1).(assigns)
@@ -277,16 +269,9 @@ defmodule Backpex.Field do
277269

278270
@impl Phoenix.LiveComponent
279271
def render(%{type: :form} = assigns) do
280-
if Backpex.Field.readonly?(assigns.field_options, assigns) do
281-
case Map.get(assigns.field_options, :render_form_readonly) do
282-
nil ->
283-
apply(__MODULE__, :render_form_readonly, [assigns])
284-
285-
func ->
286-
func.(assigns)
287-
end
288-
else
289-
Map.get(assigns.field_options, :render_form, &render_form/1).(assigns)
272+
case Map.get(assigns.field_options, :render_form) do
273+
fun when is_function(fun, 1) -> fun.(assigns)
274+
nil -> apply(__MODULE__, :render_form, [assigns])
290275
end
291276
end
292277

lib/backpex/fields/date.ex

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,8 @@ defmodule Backpex.Fields.Date do
112112
help_text={Backpex.Field.help_text(@field_options, assigns)}
113113
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
114114
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
115-
/>
116-
</Layout.field_container>
117-
</div>
118-
"""
119-
end
120-
121-
@impl Backpex.Field
122-
def render_form_readonly(assigns) do
123-
~H"""
124-
<div>
125-
<Layout.field_container>
126-
<:label align={Backpex.Field.align_label(@field_options, assigns, :top)}>
127-
<Layout.input_label text={@field_options[:label]} />
128-
</:label>
129-
<BackpexForm.input
130-
type="date"
131-
field={@form[@name]}
132-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
133-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
134-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
135-
readonly
136-
disabled
115+
readonly={@readonly}
116+
disabled={@readonly}
137117
/>
138118
</Layout.field_container>
139119
</div>

lib/backpex/fields/date_time.ex

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,8 @@ defmodule Backpex.Fields.DateTime do
112112
help_text={Backpex.Field.help_text(@field_options, assigns)}
113113
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
114114
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
115-
/>
116-
</Layout.field_container>
117-
</div>
118-
"""
119-
end
120-
121-
@impl Backpex.Field
122-
def render_form_readonly(assigns) do
123-
~H"""
124-
<div>
125-
<Layout.field_container>
126-
<:label align={Backpex.Field.align_label(@field_options, assigns, :top)}>
127-
<Layout.input_label text={@field_options[:label]} />
128-
</:label>
129-
<BackpexForm.input
130-
type="datetime-local"
131-
field={@form[@name]}
132-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
133-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
134-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
135-
readonly
136-
disabled
115+
readonly={@readonly}
116+
disabled={@readonly}
137117
/>
138118
</Layout.field_container>
139119
</div>

lib/backpex/fields/number.ex

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,14 @@ defmodule Backpex.Fields.Number do
4848
<Layout.input_label text={@field_options[:label]} />
4949
</:label>
5050
<BackpexForm.input
51-
type="text"
51+
type="number"
5252
field={@form[@name]}
5353
placeholder={@field_options[:placeholder]}
5454
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
5555
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
5656
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
57-
/>
58-
</Layout.field_container>
59-
</div>
60-
"""
61-
end
62-
63-
@impl Backpex.Field
64-
def render_form_readonly(assigns) do
65-
~H"""
66-
<div>
67-
<Layout.field_container>
68-
<:label align={Backpex.Field.align_label(@field_options, assigns)}>
69-
<Layout.input_label text={@field_options[:label]} />
70-
</:label>
71-
<BackpexForm.input
72-
type="text"
73-
field={@form[@name]}
74-
placeholder={@field_options[:placeholder]}
75-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
76-
help_text={Backpex.Field.help_text(@field_options, assigns)}
77-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
78-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
79-
readonly
80-
disabled
57+
readonly={@readonly}
58+
disabled={@readonly}
8159
/>
8260
</Layout.field_container>
8361
</div>
@@ -98,7 +76,7 @@ defmodule Backpex.Fields.Number do
9876
<.form for={@form} class="relative" phx-change="update-field" phx-submit="update-field" phx-target={@myself}>
9977
<BackpexForm.input
10078
id={"index-form-input-#{@name}-#{LiveResource.primary_value(@item, @live_resource)}"}
101-
type="text"
79+
type="number"
10280
field={@form[:value]}
10381
placeholder={@field_options[:placeholder]}
10482
input_class={["input input-sm", @valid && "not-hover:input-ghost", !@valid && "input-error bg-error/10"]}

lib/backpex/fields/text.ex

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,29 +54,8 @@ defmodule Backpex.Fields.Text do
5454
help_text={Backpex.Field.help_text(@field_options, assigns)}
5555
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
5656
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
57-
/>
58-
</Layout.field_container>
59-
</div>
60-
"""
61-
end
62-
63-
@impl Backpex.Field
64-
def render_form_readonly(assigns) do
65-
~H"""
66-
<div>
67-
<Layout.field_container>
68-
<:label align={Backpex.Field.align_label(@field_options, assigns, :center)}>
69-
<Layout.input_label text={@field_options[:label]} />
70-
</:label>
71-
<BackpexForm.input
72-
type="text"
73-
field={@form[@name]}
74-
placeholder={@field_options[:placeholder]}
75-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
76-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
77-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
78-
readonly
79-
disabled
57+
readonly={@readonly}
58+
disabled={@readonly}
8059
/>
8160
</Layout.field_container>
8261
</div>

lib/backpex/fields/textarea.ex

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,8 @@ defmodule Backpex.Fields.Textarea do
6464
help_text={Backpex.Field.help_text(@field_options, assigns)}
6565
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
6666
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
67-
/>
68-
</Layout.field_container>
69-
</div>
70-
"""
71-
end
72-
73-
@impl Backpex.Field
74-
def render_form_readonly(assigns) do
75-
~H"""
76-
<div>
77-
<Layout.field_container>
78-
<:label align={Backpex.Field.align_label(@field_options, assigns, :top)}>
79-
<Layout.input_label text={@field_options[:label]} />
80-
</:label>
81-
<BackpexForm.input
82-
type="textarea"
83-
field={@form[@name]}
84-
placeholder={@field_options[:placeholder]}
85-
rows={@field_options[:rows]}
86-
translate_error_fun={Backpex.Field.translate_error_fun(@field_options, assigns)}
87-
phx-debounce={Backpex.Field.debounce(@field_options, assigns)}
88-
phx-throttle={Backpex.Field.throttle(@field_options, assigns)}
89-
readonly
90-
disabled
67+
readonly={@readonly}
68+
disabled={@readonly}
9169
/>
9270
</Layout.field_container>
9371
</div>

0 commit comments

Comments
 (0)