Skip to content

Commit 0e22b90

Browse files
committed
bug #593 [Live] Fix bug where child components would sometimes fail this step (weaverryan)
This PR was merged into the 2.x branch. Discussion ---------- [Live] Fix bug where child components would sometimes fail this step | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fix #537instead --> | License | MIT The error would be: Failed to execute 'replaceChild' on 'Node': The node to be replaced is not a child of this node. I'm not sure what caused that (it seemed to be related to using a non-div root child component element), but replaceWith() works great. Also updated the outdated js built file. Commits ------- b658bbd [Live] Fix bug where child components would sometimes fail this step
2 parents 9a1e318 + b658bbd commit 0e22b90

File tree

2 files changed

+24
-9
lines changed

2 files changed

+24
-9
lines changed

src/LiveComponent/assets/dist/live_controller.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,15 @@ function htmlToElement(html) {
286286
const template = document.createElement('template');
287287
html = html.trim();
288288
template.innerHTML = html;
289-
const child = template.content.firstChild;
289+
if (template.content.childElementCount > 1) {
290+
throw new Error(`Component HTML contains ${template.content.childElementCount} elements, but only 1 root element is allowed.`);
291+
}
292+
const child = template.content.firstElementChild;
290293
if (!child) {
291294
throw new Error('Child not found');
292295
}
293296
if (!(child instanceof HTMLElement)) {
294-
throw new Error(`Created element is not an Element from HTML: ${html.trim()}`);
297+
throw new Error(`Created element is not an HTMLElement: ${html.trim()}`);
295298
}
296299
return child;
297300
}
@@ -1194,7 +1197,7 @@ function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements,
11941197
const childComponentToElement = findChildComponent(childComponent.id, rootToElement);
11951198
if (childComponentToElement && childComponentToElement.tagName !== childComponent.element.tagName) {
11961199
const newTag = cloneElementWithNewTagName(childComponentToElement, childComponent.element.tagName);
1197-
rootToElement.replaceChild(newTag, childComponentToElement);
1200+
childComponentToElement.replaceWith(newTag);
11981201
}
11991202
});
12001203
morphdom(rootFromElement, rootToElement, {
@@ -1219,7 +1222,7 @@ function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements,
12191222
if (modifiedFieldElements.includes(fromEl)) {
12201223
setValueOnElement(toEl, getElementValue(fromEl));
12211224
}
1222-
if (fromEl.isEqualNode(toEl)) {
1225+
if (fromEl instanceof HTMLElement && toEl instanceof HTMLElement && fromEl.isEqualNode(toEl)) {
12231226
const normalizedFromEl = cloneHTMLElement(fromEl);
12241227
normalizeAttributesForComparison(normalizedFromEl);
12251228
const normalizedToEl = cloneHTMLElement(toEl);
@@ -1424,7 +1427,7 @@ class Component {
14241427
}
14251428
return this.valueStore.get(modelName);
14261429
}
1427-
action(name, args, debounce = false) {
1430+
action(name, args = {}, debounce = false) {
14281431
const promise = this.nextRequestPromise;
14291432
this.pendingActions.push({
14301433
name,
@@ -1516,15 +1519,20 @@ class Component {
15161519
this.isRequestPending = false;
15171520
this.backendRequest.promise.then(async (response) => {
15181521
const backendResponse = new BackendResponse(response);
1519-
thisPromiseResolve(backendResponse);
15201522
const html = await backendResponse.getBody();
15211523
const headers = backendResponse.response.headers;
15221524
if (headers.get('Content-Type') !== 'application/vnd.live-component+html' && !headers.get('X-Live-Redirect')) {
1523-
this.renderError(html);
1525+
const controls = { displayError: true };
1526+
this.hooks.triggerHook('response:error', backendResponse, controls);
1527+
if (controls.displayError) {
1528+
this.renderError(html);
1529+
}
1530+
thisPromiseResolve(backendResponse);
15241531
return response;
15251532
}
15261533
this.processRerender(html, backendResponse);
15271534
this.backendRequest = null;
1535+
thisPromiseResolve(backendResponse);
15281536
if (this.isRequestPending) {
15291537
this.isRequestPending = false;
15301538
this.performRequest();
@@ -1552,7 +1560,14 @@ class Component {
15521560
this.valueStore.updatedModels.forEach((modelName) => {
15531561
modifiedModelValues[modelName] = this.valueStore.get(modelName);
15541562
});
1555-
const newElement = htmlToElement(html);
1563+
let newElement;
1564+
try {
1565+
newElement = htmlToElement(html);
1566+
}
1567+
catch (error) {
1568+
console.error('There was a problem with the component HTML returned:');
1569+
throw error;
1570+
}
15561571
this.hooks.triggerHook('loading.state:finished', newElement);
15571572
this.valueStore.reinitializeData(this.elementDriver.getComponentData(newElement));
15581573
executeMorphdom(this.element, newElement, this.unsyncedInputsTracker.getUnsyncedInputs(), (element) => getValueFromElement(element, this.valueStore), Array.from(this.getChildren().values()), this.elementDriver.findChildComponentElement, this.elementDriver.getKeyFromElement);

src/LiveComponent/assets/src/morphdom.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function executeMorphdom(
2323
// we need to "correct" the tag name for the child to match the "from"
2424
// so that we always get a "diff", not a remove/add
2525
const newTag = cloneElementWithNewTagName(childComponentToElement, childComponent.element.tagName);
26-
rootToElement.replaceChild(newTag, childComponentToElement);
26+
childComponentToElement.replaceWith(newTag);
2727
}
2828
});
2929

0 commit comments

Comments
 (0)