diff --git a/plugins/utils.ts b/plugins/utils.ts index d51f124..7715bea 100644 --- a/plugins/utils.ts +++ b/plugins/utils.ts @@ -1,5 +1,5 @@ import type { ASTv1 } from '@glimmer/syntax'; -import { SYMBOLS } from './symbols'; +import { EVENT_TYPE, SYMBOLS } from './symbols'; import type { Flags } from './flags'; import type { ComplexJSType } from './converter'; @@ -340,6 +340,9 @@ function hasStableChildsForControlNode( if ('isControl' in child) { hasStableChild = false; } else { + if (child.events.filter(([id]) => id === EVENT_TYPE.ON_CREATED).length) { + return false; + } hasStableChild = true; } } diff --git a/src/tests/integration/each-test.gts b/src/tests/integration/each-test.gts index 6aee7bb..a742d2d 100644 --- a/src/tests/integration/each-test.gts +++ b/src/tests/integration/each-test.gts @@ -1,7 +1,7 @@ import { module, test } from 'qunit'; import { render, rerender, click } from '@/tests/utils'; import { cell } from '@lifeart/gxt'; -import { type Cell } from '@/utils/reactive'; +import { formula, type Cell } from '@/utils/reactive'; import { step } from '../utils'; module('Integration | InternalComponent | each', function (hooks) { @@ -21,6 +21,52 @@ module('Integration | InternalComponent | each', function (hooks) { } } + test('it properly remove all list items if its rendered with update', async function (assert) { + function toNamedObject(arr: number[]) { + return arr.map((el) => { + return { name: el }; + }); + } + const items = cell(toNamedObject([1])); + const isFirstRender = cell(true); + + function PageOne() { + const isExpanded = formula(() => { + return items.value.length === 3 || isFirstRender.value === true; + }); + const toggle = () => { + if (items.value.length === 3) { + items.update(toNamedObject([2])); + } else { + items.update(toNamedObject([1, 2, 3])); + } + }; + return ; + } + await render(); + assert.dom('[data-test-item]').exists({ count: 1 }, 'Render first item'); + items.update(toNamedObject([1, 2, 3])); + isFirstRender.update(false); + await rerender(); + assert.dom('[data-test-item]').exists({ count: 3 }, 'Render all items'); + await click('button'); + assert.dom('[data-test-item]').doesNotExist('No list tails left'); + assert.dom('[data-test-not-expanded]').exists({ count: 1 }, 'if toggled'); + }); + test('remove first element', async function (assert) { const amountOfItemsToTest = 10; const items = cell( diff --git a/src/utils/control-flow/list.ts b/src/utils/control-flow/list.ts index 1fcb132..d023a41 100644 --- a/src/utils/control-flow/list.ts +++ b/src/utils/control-flow/list.ts @@ -344,6 +344,7 @@ export class SyncListComponent< ) { super(params, outlet, topMarker); associateDestroyable(params.ctx, [ + () => this.syncList([]), opcodeFor(this.tag, (value) => { this.syncList(value as T[]); }), @@ -426,6 +427,9 @@ export class AsyncListComponent< ) { super(params, outlet, topMarker); associateDestroyable(params.ctx, [ + async () => { + await this.syncList([]); + }, opcodeFor(this.tag, async (value) => { await this.syncList(value as T[]); }), diff --git a/src/utils/helpers/-private.ts b/src/utils/helpers/-private.ts new file mode 100644 index 0000000..06943c0 --- /dev/null +++ b/src/utils/helpers/-private.ts @@ -0,0 +1,10 @@ +import type { AnyCell } from "../reactive"; +import { isTagLike } from "../shared"; + +export function isTag(arg: unknown): arg is AnyCell { + if (typeof arg === 'object' && arg !== null && isTagLike(arg)) { + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/src/utils/helpers/and.ts b/src/utils/helpers/and.ts index e64cd12..0d93281 100644 --- a/src/utils/helpers/and.ts +++ b/src/utils/helpers/and.ts @@ -1,3 +1,10 @@ +import { isTag } from "./-private"; + export function $__and(...args: unknown[]) { - return args.every((arg) => !!arg); + return args.every((arg) => { + if (isTag(arg)) { + return !!arg.value; + } + return !!arg; + }); } diff --git a/src/utils/helpers/eq.ts b/src/utils/helpers/eq.ts index faf3fee..1e1d07a 100644 --- a/src/utils/helpers/eq.ts +++ b/src/utils/helpers/eq.ts @@ -1,4 +1,6 @@ +import { isTag } from "./-private"; + export function $__eq(...args: unknown[]) { - const firstValue = args[0]; - return args.every((arg) => arg === firstValue); + const firstValue = isTag(args[0]) ? args[0].value : args[0]; + return args.every((arg) => (isTag(arg) ? arg.value : arg) === firstValue); } diff --git a/src/utils/helpers/if.ts b/src/utils/helpers/if.ts index 0c28b26..765099b 100644 --- a/src/utils/helpers/if.ts +++ b/src/utils/helpers/if.ts @@ -1,11 +1,12 @@ -import { type AnyCell } from '../reactive'; +import { isTag } from "./-private"; + export function $__if( condition: unknown, ifTrue: unknown, ifFalse: unknown = '', ) { - if (typeof condition === 'object') { - return (condition as AnyCell).value ? ifTrue : ifFalse; + if (isTag(condition)) { + return condition.value ? ifTrue : ifFalse; } return condition ? ifTrue : ifFalse; } diff --git a/src/utils/helpers/not.ts b/src/utils/helpers/not.ts index 4bef1f7..2771ad7 100644 --- a/src/utils/helpers/not.ts +++ b/src/utils/helpers/not.ts @@ -1,4 +1,9 @@ +import { isTag } from "./-private"; + export function $__not(arg: unknown) { + if (isTag(arg)) { + return !arg.value; + } return !arg; } \ No newline at end of file diff --git a/src/utils/helpers/or.ts b/src/utils/helpers/or.ts index fd9d773..213581e 100644 --- a/src/utils/helpers/or.ts +++ b/src/utils/helpers/or.ts @@ -1,4 +1,8 @@ +import { isTag } from "./-private"; + export function $__or(...args: unknown[]) { - return args.find((arg) => !!arg); + return args.find((arg) => { + return isTag(arg) ? !!arg.value : !!arg; + }); } \ No newline at end of file