Skip to content

Commit

Permalink
[Live] Fixing a bug where parent component model would be used to set…
Browse files Browse the repository at this point in the history
… child model field
  • Loading branch information
weaverryan committed Dec 7, 2022
1 parent 6040306 commit 9c870b4
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 8 deletions.
3 changes: 3 additions & 0 deletions src/LiveComponent/assets/dist/live_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2112,6 +2112,9 @@ class SetValueOntoModelFieldsPlugin {
if (element instanceof HTMLFormElement) {
return;
}
if (!elementBelongsToThisComponent(element, component)) {
return;
}
const modelDirective = getModelDirectiveFromElement(element);
if (!modelDirective) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Component from '../index';
import {
elementBelongsToThisComponent,
getModelDirectiveFromElement,
getValueFromElement,
setValueOnElement
Expand Down Expand Up @@ -38,6 +39,10 @@ export default class implements PluginInterface {
return;
}

if (!elementBelongsToThisComponent(element, component)) {
return;
}

const modelDirective = getModelDirectiveFromElement(element);
if (!modelDirective) {
return;
Expand Down
39 changes: 39 additions & 0 deletions src/LiveComponent/assets/test/controller/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,45 @@ describe('LiveController data-model Tests', () => {
expect(commentField.value).toEqual('MMMM SO GOOD');
});

it('does not try to set the value of inputs inside a child component', async () => {
const test = await createTest({ comment: 'cookie', childComment: 'mmmm' }, (data: any) => `
<div ${initComponent(data)}>
<textarea data-model="comment" id="parent-comment"></textarea>
<div ${initComponent({ comment: data.childComment }, {}, {id: 'the-child-id'})}>
<textarea data-model="comment" id="child-comment"></textarea>
</div>
</div>
`);

const commentField = test.element.querySelector('#parent-comment');
if (!(commentField instanceof HTMLTextAreaElement)) {
throw new Error('wrong type');
}
expect(commentField.value).toEqual('cookie');

const childCommentField = test.element.querySelector('#child-comment');
if (!(childCommentField instanceof HTMLTextAreaElement)) {
throw new Error('wrong type');
}
expect(childCommentField.value).toEqual('mmmm');

// NOW we will re-render
test.expectsAjaxCall('get')
.expectSentData(test.initialData)
// change the data to be extra tricky
.serverWillChangeData((data) => {
data.comment = 'i like apples';
})
.init();

await test.component.render();

expect(commentField.value).toEqual('i like apples');
// child component should not have been re-rendered
expect(childCommentField.value).toEqual('mmmm');
});

it('keeps the unsynced value of an input on re-render, but accepts other changes to the field', async () => {
const test = await createTest({
comment: 'Live components',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
*/
final class AddLiveAttributesSubscriberTest extends KernelTestCase
{
use HasBrowser;
/**
* The deterministic id of the "todo_item" components in todo_list.html.twig.
* If that template changes, this will need to be updated.
*/
public const TODO_ITEM_DETERMINISTIC_PREFIX = 'live-289310975-';

use HasBrowser;

public function testInitLiveComponent(): void
{
$div = $this->browser()
Expand Down Expand Up @@ -91,8 +90,8 @@ public function testItAddsIdAndFingerprintToChildComponent(): void

$lis = $ul->children('li');
// deterministic id: should not change, and counter should increase
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX . '0', $lis->first()->attr('data-live-id'));
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX . '2', $lis->last()->attr('data-live-id'));
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX.'0', $lis->first()->attr('data-live-id'));
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX.'2', $lis->last()->attr('data-live-id'));

// fingerprints
// first and last both have the same input - thus fingerprint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ final class InterceptChildComponentRenderSubscriberTest extends KernelTestCase
// if you pass in 3 "items" with data that matches what's used by default
// in buildUrlForTodoListComponent
private static array $actualTodoItemFingerprints = [
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '0' => 'LwqODySoRx3q+v64EzalGouzpSHWKIm0jENTUGtQloE=',
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '1' => 'gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o=',
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '2' => 'ndV00y/qOSH11bjOKGDJVRsxANtbudYB6K8D46viUI8=',
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX.'0' => 'LwqODySoRx3q+v64EzalGouzpSHWKIm0jENTUGtQloE=',
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX.'1' => 'gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o=',
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX.'2' => 'ndV00y/qOSH11bjOKGDJVRsxANtbudYB6K8D46viUI8=',
];

public function testItAllowsFullChildRenderOnMissingFingerprints(): void
Expand Down Expand Up @@ -79,7 +79,7 @@ public function testItRendersEmptyElementOnMatchingFingerprintWithCustomDataLive
public function testItRendersNewPropWhenFingerprintDoesNotMatch(): void
{
$fingerprints = self::$actualTodoItemFingerprints;
$fingerprints[AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '1'] = 'wrong fingerprint';
$fingerprints[AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX.'1'] = 'wrong fingerprint';

$this->browser()
->visit($this->buildUrlForTodoListComponent($fingerprints))
Expand Down

0 comments on commit 9c870b4

Please sign in to comment.