Error in $doc.update()?

I'm not quite sure if I'm doing it wrong. The error I'm getting is:
foundry.js:13637 Uncaught (in promise) Error: You must provide an _id for every object in the update data Array.
at #preUpdateDocumentArray (foundry.js:13637:32)
at ClientDatabaseBackend._updateDocuments (foundry.js:13449:73)
at ClientDatabaseBackend.update (commons.js:8677:19)
at async Actor5e.updateDocuments (commons.js:8001:23)
at async Actor5e.update (commons.js:8098:23)
foundry.js:13637 Uncaught (in promise) Error: You must provide an _id for every object in the update data Array.
at #preUpdateDocumentArray (foundry.js:13637:32)
at ClientDatabaseBackend._updateDocuments (foundry.js:13449:73)
at ClientDatabaseBackend.update (commons.js:8677:19)
at async Actor5e.updateDocuments (commons.js:8001:23)
at async Actor5e.update (commons.js:8098:23)
Here's the component
<script>
import { log, update } from "~/src/helpers/Utility";
import { Timing } from "@typhonjs-fvtt/runtime/util";
import { createEventDispatcher, getContext, onDestroy, onMount } from "svelte";

export let document = false;

const dispatch = createEventDispatcher();
const doc = document || getContext("#doc");
const updateDebounce = (path) => Timing.debounce($doc.update({[path]: event.target.value }), 300);

$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);

</script>

<template lang="pug">
.attribute-entry.mt-sm
+each("systemAbilitiesArray as ability, index")
.flexrow.mb-sm
.flex1 {ability[1].label}
.flex3.right
input(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input="{updateDebounce(`system.abilities.${ability[1].abbreviation}.value`)}" style="width: 40px")

</template>
<script>
import { log, update } from "~/src/helpers/Utility";
import { Timing } from "@typhonjs-fvtt/runtime/util";
import { createEventDispatcher, getContext, onDestroy, onMount } from "svelte";

export let document = false;

const dispatch = createEventDispatcher();
const doc = document || getContext("#doc");
const updateDebounce = (path) => Timing.debounce($doc.update({[path]: event.target.value }), 300);

$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);

</script>

<template lang="pug">
.attribute-entry.mt-sm
+each("systemAbilitiesArray as ability, index")
.flexrow.mb-sm
.flex1 {ability[1].label}
.flex3.right
input(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input="{updateDebounce(`system.abilities.${ability[1].abbreviation}.value`)}" style="width: 40px")

</template>
Normally (e.g. in my system, as opposed to this module) $doc.update() just works for me. I haven't seen this _id error before. It seems weird because update is being called on $doc, which is a TJSDocument instance of the actor, so it has the _id already I would expect.
6 Replies
geoidesic [Aardvark Games]
Ahh.. nvm. The actor is in memory, so I need to use updateSource() not update()
TyphonJS (Michael)
You are also using Timing.debounce incorrectly. It should be:
const updateDebounce = Timing.debounce((path) => $doc.update({[path]: event.target.value }), 300);
const updateDebounce = Timing.debounce((path) => $doc.update({[path]: event.target.value }), 300);
Timing.debounce is a higher order function that returns a function with the specified debounce functionality around the callback / function specified as the first argument.
geoidesic [Aardvark Games]
I ended up with this:
<script>
import { log, update } from "~/src/helpers/Utility";
import { Timing } from "@typhonjs-fvtt/runtime/util";
import { createEventDispatcher, getContext, onDestroy, onMount } from "svelte";

export let document = false;

const dispatch = createEventDispatcher();
const doc = document || getContext("#doc");
const updateDebounce = Timing.debounce(updateValue, 300);

function updateValue(attr, event) {
const options = {system: {abilities: { [attr]: {value: Number(event.target.value)}}}};
$doc.updateSource(options)
$doc = $doc //<--- necessary for reactivity in last line of template
}

$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);

</script>

<template lang="pug">
.attribute-entry.mt-sm
.flexrow.mb-sm
.flex3.left Ability
.flex1.center Score
.flex1.center Modifier
+each("systemAbilitiesArray as ability, index")
.flexrow.mb-sm
.flex3.left {ability[1].label}
.flex1.center
input.center(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input!="{updateDebounce(ability[1].abbreviation, event)}")
.flex1.center.align-text-with-input {$doc.system.abilities[ability[1].abbreviation].mod}
</template>
<script>
import { log, update } from "~/src/helpers/Utility";
import { Timing } from "@typhonjs-fvtt/runtime/util";
import { createEventDispatcher, getContext, onDestroy, onMount } from "svelte";

export let document = false;

const dispatch = createEventDispatcher();
const doc = document || getContext("#doc");
const updateDebounce = Timing.debounce(updateValue, 300);

function updateValue(attr, event) {
const options = {system: {abilities: { [attr]: {value: Number(event.target.value)}}}};
$doc.updateSource(options)
$doc = $doc //<--- necessary for reactivity in last line of template
}

$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);

</script>

<template lang="pug">
.attribute-entry.mt-sm
.flexrow.mb-sm
.flex3.left Ability
.flex1.center Score
.flex1.center Modifier
+each("systemAbilitiesArray as ability, index")
.flexrow.mb-sm
.flex3.left {ability[1].label}
.flex1.center
input.center(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input!="{updateDebounce(ability[1].abbreviation, event)}")
.flex1.center.align-text-with-input {$doc.system.abilities[ability[1].abbreviation].mod}
</template>
The $doc = $doc – is needed to make the mod value (last line of the template) reactive (from https://learn.svelte.dev/tutorial/updating-arrays-and-objects) It feels inelegant. Is there a better way to facilitate nested reactivity when using $doc.update() or $doc.updateSource()?
TyphonJS (Michael)
Presently, no because $doc.updateSource doesn't trigger the DB update response / callbacks. However, I'll look into adding an updateSource method directly to TJSDocument which will invoke the underlying updateSource of any wrapped core document, but will also trigger reactivity for in memory changes. In this case you'll drop the $ and it will just be doc.updateSource(...). I might also add a correponding update method to TJSDocument. So, in 0.2.0 there should be a way to automatically trigger reactivity w/ TJSDocument for in memory usage.
geoidesic [Aardvark Games]
Is there a new way to do this now? I think my above code no longer works in 0.3.0. I've mentioned this in my forum post about the upgrade but this might be a better place to discuss it. I can't help wondering if svelte 5 runes wouldn't be useful here. Tried a few things... this seems to work:
await $doc.updateSource(options);
await tick();

if ($doc.render) {
$doc.render();
}
await $doc.updateSource(options);
await tick();

if ($doc.render) {
$doc.render();
}
`
TyphonJS (Michael)
I can't really debug your code. First as always by using PUG this provides a barrier to sight-read your code to see any obvious mistakes. The code you write may have many bugs / inefficiencies in it that you are unaware of.. For instance this looks wrong:
input.center(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input!="{updateDebounce(ability[1].abbreviation, event)}")
input.center(type="number" value="{$doc.system.abilities[ability[1].abbreviation].value}" on:input!="{updateDebounce(ability[1].abbreviation, event)}")
Particularly this on:input!="{updateDebounce(ability[1].abbreviation, event)}") It looks like you are invoking once the function instead of assigning an arrow function like on:input={(event) => updateDebounce(/* stuff */)} This is not reactive:
$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);
$: systemAbilities = game.system.config.abilities
$: systemAbilitiesArray = Object.entries(systemAbilities);
TRL likely is not broken, but your code may easily have one or more problems that you are generally unaware of how the interactions are or are not occuring. Learning to debug your own code is important. Divide and conquer is generally how you should approach this process. Verify each half / step and ensure that you are getting the values you expect before looking at the other half of code.

Did you find this page helpful?