T
TyphonJSTyphonJS (Michael)

TJSDocument discussion w/ VoodooFrog

Following up on this discussion from #dev-lounge from @VoodooFrog:
I have a TJSDocument for an actor in which I am creating an embedded store which filters out certain items on the actor. These are then each displayed with their own component which contains a select that sets some data. Now, I have created a new select control in the parent that sets all the items via an update to a given value, but upon doing so the child component is not reflecting the change in their own controls (and yes I am setting the selected value based on the data). Any ideas what might be happening or how I can fix it? I don't have the code in my repo yet, as I'm still working on it so I can't just link the source, unfortunately.
When you can commit something to take a look at that will help a lot.
V
VoodooFrog53d ago
https://gist.github.com/voodoofrog/357679ea18777710a18a2e8888b668ca I'll Gist it, so I don't have to commit yet. If you need to see the subcomponent, just ask and I'll put that up too. Well, actually - I think the one in the repo will be fine, I haven't made any changes to that. https://github.com/voodoofrog/vf-5e-sheet-addons/blob/master/src/components/SpellManagementComponent.svelte
TM
TyphonJS (Michael)53d ago
I think the problem is that you are updating the embedded Item documents directly. The embedded collection is only reactive for changes of the collection itself and not the items / documents that it contains. It is not recommended to make TJSDocument instances for each spell / embedded collection document. What you can likely do is force an update to the embedded collection / DynMapReducer instance and this should cause the #each loop to run again: So this:
const onChangeSelect = async (value) => {
updating = true;
for (const spell of spells) {
await spell.update({ [`flags.${MODULE_ID}.${FLAGS.SPELL_SOURCE}`]: value });
}
updating = false;

// This should work and force (true) an update to the reducer index and notify subscribers.
// So the `#each` block should run again.
spells.index.update(true);
};
const onChangeSelect = async (value) => {
updating = true;
for (const spell of spells) {
await spell.update({ [`flags.${MODULE_ID}.${FLAGS.SPELL_SOURCE}`]: value });
}
updating = false;

// This should work and force (true) an update to the reducer index and notify subscribers.
// So the `#each` block should run again.
spells.index.update(true);
};
You might also be able to this with a reactive statement after redefining spells with let instead of const.
$: if (!updating) { spells = spells; }
$: if (!updating) { spells = spells; }
I'm only taking a stab in the dark here more or less. But it looks like you are updating the embedded documents, but the spells reactive embedded collection is only reactive for the collection and not the documents contained.
V
VoodooFrog53d ago
Yeah, I'm only wrapping TJSDocument around the actor. I figured doing so for each spell would be massive overkill. And I am indeed updating the items individually and not the collection, I figured there would be some way to get the collection to refresh after the updates, but that's where I got stuck. Hm... spells.index.update(true) still doesn't do it. 😦 I'll try redefining... ...still no. Hm. I feel like I'm missing something obvious.
TM
TyphonJS (Michael)53d ago
You can put in a reactive log statement that should trigger when the embedded collection subscribers are notified:
$: console.log(`!!!! spells count: ${[...$spells].length}`).
$: console.log(`!!!! spells count: ${[...$spells].length}`).
Also make sure the previous reactive statement is triggering correctly by putting a console.log there.
$: if (!updating) {
console.log(`!!! Reassigning 'spells'`)
spells = spells;
}
$: if (!updating) {
console.log(`!!! Reassigning 'spells'`)
spells = spells;
}
V
VoodooFrog53d ago
I see both log statements on a change to the select. 🤷‍♂️ Have I screwed up my selected conditions in the subcomponent somehow? I know for sure that a hot-reload via code change switches them all correctly... but that doesn't really tell me much.
TM
TyphonJS (Michael)53d ago
You can put in this statement in the #each block to have things log when the each block runs:
{#each [...$spells] as spell (spell.id)}
{(console.log('#each block running'), '')}
<SpellManagementComponent {spell} {classes} {mode} />
{/each}
{#each [...$spells] as spell (spell.id)}
{(console.log('#each block running'), '')}
<SpellManagementComponent {spell} {classes} {mode} />
{/each}
It will log repeatedly for the count of all the spells, but at least you can determine if the each block is running correctly then it very well could be a problem in SpellManagementComponent.'
V
VoodooFrog53d ago
(7) SpellbookManagerShell.svelte:48 !!!! spells count: 7
SpellbookManagerShell.svelte:44 !!! Reassigning 'spells'
SpellbookManagerShell.svelte:48 !!!! spells count: 7
(7) SpellbookManagerShell.svelte:48 !!!! spells count: 7
SpellbookManagerShell.svelte:44 !!! Reassigning 'spells'
SpellbookManagerShell.svelte:48 !!!! spells count: 7
It doesn't appear to have run. Although could that not also point to a problem with the each, like not being notified? I've also just realised if I wrap the each in an if using the updated var, then I can force it to re-evalute... but it looks a bit naff as it disappears for a moment before coming back.
TM
TyphonJS (Michael)53d ago
Yeah.. A bit of a trick since the spells embedded collection is not changing. You can likely use a keyed block and that will work as intended without the if conditional.
{#key updating}
{#each $spells as spell (spell.id)}
<SpellManagementComponent {spell} {classes} {mode} />
{/each}
{/key}
{#key updating}
{#each $spells as spell (spell.id)}
<SpellManagementComponent {spell} {classes} {mode} />
{/each}
{/key}
You might have noticed dropping the [...$spells] array conversion. The latest Svelte should accept an iterator so you can use $spells in the #each block. Svelte does the array conversion internally; just syntactic sugar in this case.
V
VoodooFrog53d ago
Yeah, I can't get away with dropping the conversion. I'm on svelte 4.2.0. IIRC, I ran into dependency issues if I tried to go higher. But they key block has done the trick, thanks @TyphonJS (Michael) [UTC-7]! 🙂
TM
TyphonJS (Michael)53d ago
I think in this particular case it's the best solution. We'll see what Svelte 5 can bring to the table. It is tricky to reactively monitor changes to documents in an embedded collection. The original #each block with the #key block will run if you added / removed spell items from the collection. I really wish the Foundry document model had a streamlined subscription mechanism vs the semi-heavyweight workaround w/ TJSDocument being required. TRL 0.2.0 does make TJSDocument more friendly / less error prone. One downside right now that is going away is that for the actorDoc you need to manually destroy that when the app shell closes via an onDestroy callback like:
import { onDestroy } from 'svelte';

const actorDoc = new TJSDocument(actor);

onDestroy(() => actorDoc.destroy());
import { onDestroy } from 'svelte';

const actorDoc = new TJSDocument(actor);

onDestroy(() => actorDoc.destroy());
In 0.2.0 TJSDocument is smart enough to connect to the Foundry document model only when there are subscriptions, so it will automatically clean up itself.
V
VoodooFrog53d ago
Thanks for all your help.
TM
TyphonJS (Michael)53d ago
Yeah.. And you might just play around with using TJSDocument in SpellManagementComponent for the spell document. You just have to use the onDestroy callback for now otherwise things could get messy quick / dangling listener issue w/ the connection to the Foundry document. If the amount of spell items isn't expected to be super massive it should work OK.. Just going to be less error prone / less code in 0.2.0. A lot of this will evolve through the Svelte 5 transition.
V
VoodooFrog53d ago
Good to hear, I'll look forward to it. I'm pretty sure I had a TJSDoc for each spell in an earlier iteration, but like I said earlier I figured it was overkill.
TM
TyphonJS (Michael)53d ago
It can be, but depending on the goal / complexity of the UI for the embedded documents could be handy. Better to wait until 0.2.0 for the peace of mind / not having to manually destroy TJSDocument instances.
V
VoodooFrog53d ago
Frankly, even with manual cleanup wrinkles, the current release of TRL is still miles better than dealing with handlebars.
ES
El Saico52d ago
stumbles on the conversation This is what brought me to TRL; having liked Vue's reactive+component model, Svelte feels like the perfect alternative for a presentation layer library. Also, Tailwind (which became love at first try) suits reusable components much better.
V
VoodooFrog52d ago
I use Tailwind at work... haven't tried using it in a Foundry module yet. Mind you, the module discussed here is copying default 5e sheet styles anyway, so there'd not be much point in it. I do like Tailwind, though.
Want results from more Discord servers?
Add your server
More Posts
Advanced Templates Module (PF1)A thread to discuss Svelte and the Advanced Templates module.Automated AnimationsThis is a forum post to track discussion on Automated Animations. When TRL `0.2.0` is released I plasetPosition in SvelteApplicationThe `SvelteApplication` class has a function called `setPosition` that is there to support core behaTJSDocument delete actionWhen emitting this event, TJSDocument passes "undefined" as the first argument instead of the documeQuest Log Permissions / InfoHI @larsvonawesome. You asked: > Trying to find more info on Forien's Quest Log; I'm having an issueCreating a derived reducer for a DynMapReducerDo you have an example of how to make a derived reducer for a reducer that's not part of a TJSDcoumeQuest Log objective counts not displayingHi there. the Quest Log objectives (done/total) don't seem to change when I mark quests as completeItem Piles: AuctioneerA few people in the League & some that I know personally have commissioned an Item Piles powered aucSvelecte AlternativesI'm looking to implement a searchable list of sorts, and I found Svelecte: <https://mskocik.github.iSetting up TRL / contributing to existing module (Item Piles)Hi @Paith. I have started this forum post where we can continue to discuss setting up TRL and gettinReactivty and prepareDerivedDataFrom @Wasp > So I'm using prepareDerivedData to, well, derive some data for my items; some of them aLocal Development w/ NPM LinkFrom @Gerark: > Hello! this is probably a base webdev question but I can't find a good reference forIssues Configuring TyphonJS in TSI'm receiving this message in my TS Files: `Cannot find module '#runtime/svelte/application' or its TRL / Svelte based game systemsThis post is a list of repos for game systems built with TRL / Svelte for Foundry. Please DM me if yList of TjsdocumentsLink to original response: https://discord.com/channels/737953117999726592/1067337319041998869/11562Is there a way to export the quest log?I'm trying to setup a world for the PF2E Beginner Box but right now I working in a beta testing worFabricate@MisterPotts. Just starting a forum post to keep track of the conversation. > Fabricate doesn't maSvelte 5 TJSDocument PrototypeGreets @FVTT ▹ Developer. As some of you might have seen there is a bit of paradigm shift that is Funky interaction with fokus managementTJS's Focus Management is creating fun issues for me again. focusKeep = true causes my drag and dropHow to get rid of funky Prosemirror overlapHi. I'm getting some overlap with Prosemirror (see image) any ideas how to avoid that? ``` "@ty