Skip to content

Commit 5384eb7

Browse files
authored
evaluate js commands during form recovery (#3608)
Fixes #3607.
1 parent 9f8f631 commit 5384eb7

File tree

4 files changed

+42
-6
lines changed

4 files changed

+42
-6
lines changed

assets/js/phoenix_live_view/js.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ let JS = {
6565
let pushOpts = {loading, value, target, page_loading: !!page_loading}
6666
let targetSrc = eventType === "change" && dispatcher ? dispatcher : sourceEl
6767
let phxTarget = target || targetSrc.getAttribute(view.binding("target")) || targetSrc
68-
view.withinTargets(phxTarget, (targetView, targetCtx) => {
68+
const handler = (targetView, targetCtx) => {
6969
if(!targetView.isConnected()){ return }
7070
if(eventType === "change"){
7171
let {newCid, _target} = args
@@ -78,7 +78,14 @@ let JS = {
7878
} else {
7979
targetView.pushEvent(eventType, sourceEl, targetCtx, event || phxEvent, data, pushOpts, callback)
8080
}
81-
})
81+
}
82+
// in case of formRecovery, targetView and targetCtx are passed as argument
83+
// as they are looked up in a template element, not the real DOM
84+
if(args.targetView && args.targetCtx){
85+
handler(args.targetView, args.targetCtx)
86+
} else {
87+
view.withinTargets(phxTarget, handler)
88+
}
8289
},
8390

8491
exec_navigate(e, eventType, phxEvent, view, sourceEl, el, {href, replace}){

assets/js/phoenix_live_view/view.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import DOMPatch from "./dom_patch"
5454
import LiveUploader from "./live_uploader"
5555
import Rendered from "./rendered"
5656
import ViewHook from "./view_hook"
57+
import JS from "./js"
5758

5859
export let prependFormDataKey = (key, prefix) => {
5960
let isArray = key.endsWith("[]")
@@ -1413,10 +1414,17 @@ export default class View {
14131414
this.withinTargets(phxTarget, (targetView, targetCtx) => {
14141415
const cid = this.targetComponentID(newForm, targetCtx)
14151416
pending++
1416-
targetView.pushInput(input, targetCtx, cid, phxEvent, {_target: input.name}, () => {
1417-
pending--
1418-
if(pending === 0){ callback() }
1419-
})
1417+
let e = new CustomEvent("phx:form-recovery", {detail: {sourceElement: oldForm}})
1418+
JS.exec(e, "change", phxEvent, this, input, ["push", {
1419+
_target: input.name,
1420+
targetView,
1421+
targetCtx,
1422+
newCid: cid,
1423+
callback: () => {
1424+
pending--
1425+
if(pending === 0){ callback() }
1426+
}
1427+
}])
14201428
}, templateDom, templateDom)
14211429
}
14221430

test/e2e/support/form_live.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ defmodule Phoenix.LiveViewTest.E2E.FormLive do
8484
update(socket, :params, &Map.delete(&1, "phx-change"))
8585
end
8686

87+
def update_params(socket, %{"js-change" => _}) do
88+
update(socket, :params, &Map.put(&1, "phx-change", JS.push("validate")))
89+
end
90+
8791
def update_params(socket, _), do: socket
8892

8993
@impl Phoenix.LiveView

test/e2e/tests/forms.spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,23 @@ for(let path of ["/form/nested", "/form"]){
110110
]))
111111
})
112112

113+
test("JS command in phx-change works during recovery", async ({page}) => {
114+
await page.goto(path + "?" + additionalParams + "&js-change=1")
115+
await syncLV(page)
116+
117+
await page.locator("input[name=b]").fill("test")
118+
await expect(page.locator("form")).toHaveAttribute("phx-change", /push/)
119+
await syncLV(page)
120+
121+
await page.evaluate(() => new Promise((resolve) => window.liveSocket.disconnect(resolve)))
122+
await expect(page.locator(".phx-loading")).toHaveCount(1)
123+
124+
await page.evaluate(() => window.liveSocket.connect())
125+
await syncLV(page)
126+
await expect(page.locator(".phx-loading")).toHaveCount(0)
127+
await expect(page.locator("input[name=b]")).toHaveValue("test")
128+
})
129+
113130
test("does not recover when form is missing id", async ({page}) => {
114131
await page.goto(`${path}?no-id&${additionalParams}`)
115132
await syncLV(page)

0 commit comments

Comments
 (0)