Skip to content

Commit

Permalink
Keep all old values in __changed__ when using assign/3 instead of…
Browse files Browse the repository at this point in the history
… only maps (#3392)

This commit is a prerequisite to perform nested change tracking of other types of data than maps, eg. lists or tuples. Previously `socket.assigns.__changed__` kept old versions of maps for nested change tracking and `true` for all other types of props. Worth to note that `changed?/2` is simply checking if key is in the `__changed__` map, value itself is not used.
  • Loading branch information
Valian committed Aug 18, 2024
1 parent 7efe19c commit 1f968fc
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 12 deletions.
8 changes: 3 additions & 5 deletions lib/phoenix_live_view/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,11 @@ defmodule Phoenix.LiveView.Utils do
def force_assign(assigns, nil, key, val), do: Map.put(assigns, key, val)

def force_assign(assigns, changed, key, val) do
# If the current value is a map, we store it in changed so
# we can perform nested change tracking. Also note the use
# of put_new is important. We want to keep the original value
# We store old value in changed so we can perform nested change tracking.
# Also note the use of put_new is important. We want to keep the original value
# from assigns and not any intermediate ones that may appear.
current_val = Map.get(assigns, key)
changed_val = if is_map(current_val), do: current_val, else: true
changed = Map.put_new(changed, key, changed_val)
changed = Map.put_new(changed, key, current_val)
Map.put(%{assigns | __changed__: changed}, key, val)
end

Expand Down
31 changes: 24 additions & 7 deletions test/phoenix_component_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,23 @@ defmodule Phoenix.ComponentUnitTest do
assert socket.assigns.existing == %{foo: :bam}
assert socket.assigns.__changed__.existing == %{foo: :bar}
end

test "keeps whole lists in changes" do
socket = assign(@socket, existing: [:foo, :bar])
socket = Utils.clear_changed(socket)

socket = assign(socket, existing: [:foo, :baz])
assert socket.assigns.existing == [:foo, :baz]
assert socket.assigns.__changed__.existing == [:foo, :bar]

socket = assign(socket, existing: [:foo, :bat])
assert socket.assigns.existing == [:foo, :bat]
assert socket.assigns.__changed__.existing == [:foo, :bar]

socket = assign(socket, %{existing: [:foo, :bam]})
assert socket.assigns.existing == [:foo, :bam]
assert socket.assigns.__changed__.existing == [:foo, :bar]
end
end

describe "assign with assigns" do
Expand Down Expand Up @@ -102,7 +119,7 @@ defmodule Phoenix.ComponentUnitTest do
notexisting: "new-notexisting",
live_action: nil,
flash: %{},
__changed__: %{existing: true, notexisting: true}
__changed__: %{existing: nil, notexisting: nil}
}
end

Expand All @@ -120,7 +137,7 @@ defmodule Phoenix.ComponentUnitTest do
notexisting: "new-notexisting",
live_action: nil,
flash: %{},
__changed__: %{existing: true, notexisting: true, existing2: true}
__changed__: %{existing: nil, notexisting: nil, existing2: nil}
}
end

Expand All @@ -143,11 +160,11 @@ defmodule Phoenix.ComponentUnitTest do
live_action: nil,
flash: %{},
__changed__: %{
existing: true,
existing2: true,
notexisting: true,
notexisting2: true,
notexisting3: true
existing: nil,
existing2: nil,
notexisting: nil,
notexisting2: nil,
notexisting3: nil
}
}
end
Expand Down

0 comments on commit 1f968fc

Please sign in to comment.