Correct way to have inputs show other data?

{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
<div class="form-fields">
<input
id="colorOverride"
type="text"
bind:value={updates.data.measureTemplate.color}
name="measureTemplate.color"
/>
<div class="color-input-border">
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
bind:value={updates.data.measureTemplate.color}
data-edit="measureTemplate.color"
/>
</div>
</div>
{:else}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
<div class="form-fields">
<input id="colorOverride" type="text" value={currentUserColor} name="measureTemplate.color" />
<div class="color-input-border">
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
value={currentUserColor}
data-edit="measureTemplate.color"
/>
</div>
</div>
{/if}
{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
<div class="form-fields">
<input
id="colorOverride"
type="text"
bind:value={updates.data.measureTemplate.color}
name="measureTemplate.color"
/>
<div class="color-input-border">
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
bind:value={updates.data.measureTemplate.color}
data-edit="measureTemplate.color"
/>
</div>
</div>
{:else}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
<div class="form-fields">
<input id="colorOverride" type="text" value={currentUserColor} name="measureTemplate.color" />
<div class="color-input-border">
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
value={currentUserColor}
data-edit="measureTemplate.color"
/>
</div>
</div>
{/if}
The goal here is to show a color input. If the input is empty then it shows the user's current color. If it's showing the user's current color, then I want it t o be able to modify the color variable. This all basically works, but what it's doing is quickly updating the variable to the user's color instead of just staying blank and falling back to the user's color. This causes a double render and makes it so that if the user's color changes, then this won't be using the new value. Is there a better way to do this?
65 Replies
david aka claudekennilol
Where updates.data.measureTemplate.color is seeded with any current values when the form is opened (updates is seeded with current data when opened)
TyphonJS (Michael)
Do you have a repo link as well?
david aka claudekennilol
Not with my current changes
Vauxs
Vauxs6mo ago
I am confused as to what is causing an update here
david aka claudekennilol
I believe it's the data-edit property As I still want the input to be editable so the user can change it
Vauxs
Vauxs6mo ago
Why not use svelte to do the updates?
david aka claudekennilol
This is being dropped directly into a Item sheet in foundry Not using svelte (for the update) because I'd rather have it within the Item sheet so it's more integrated
Vauxs
Vauxs6mo ago
You are already bind:value it
david aka claudekennilol
Right, that's for the case when there is already a value Specifically the question is how to handle the else block where I want the input to be editable when there is no value, but behave as if the value is currentUserColor
Vauxs
Vauxs6mo ago
What do you mean "behave", because from what I am seeing you are just inputting the currentUserColor into the input and that may or may not be triggering the update through data-edit
david aka claudekennilol
Yes, this isn't working as I want it and I don't know the correct way to do it, hence the questi on 🙃
Vauxs
Vauxs6mo ago
If you want the color to show up as a default but not actually exist in the input, use placeholder
TyphonJS (Michael)
Just a head check... Where is updates / updates.data coming from? I know on v12 the field changed to renderData instead if this is coming through a document update / TJSDocument.
david aka claudekennilol
This is still on v11. I'm constructing it based off an Item passed to the svelte app. (technically action, but it's a basically a document in pf1)
return new TemplateSettings({
target: parent,
anchor: sibling,
props: { action, TemplateApplication: app },
});
return new TemplateSettings({
target: parent,
anchor: sibling,
props: { action, TemplateApplication: app },
});
<script>
import SharedSettings from "./shared-settings.svelte";
import { ifDebug } from "../scripts/utils";
import { prepareData } from "./prepare-template-data";

export let action = void 0;
export let TemplateApplication = void 0;

const updates = prepareData(action); <-- updates built here

ifDebug(() => console.log("initializing updates", updates));
</script>

<svelte:component this={TemplateApplication} {action} {updates}>
<SharedSettings {updates} />
</svelte:component>

<style lang="scss">
</style>
<script>
import SharedSettings from "./shared-settings.svelte";
import { ifDebug } from "../scripts/utils";
import { prepareData } from "./prepare-template-data";

export let action = void 0;
export let TemplateApplication = void 0;

const updates = prepareData(action); <-- updates built here

ifDebug(() => console.log("initializing updates", updates));
</script>

<svelte:component this={TemplateApplication} {action} {updates}>
<SharedSettings {updates} />
</svelte:component>

<style lang="scss">
</style>
--> {#if updates.data.measureTemplate.color} This line, it doesn't appear to be going off of the truthiness of color. Right now color is an empty string but it's still entering this block Either that, or it's not being rebuilt when the value is emptied because that second block isn't binding to it now
Vauxs
Vauxs6mo ago
Oh. If types are the issue then why not Boolean(updates.data.measureTemplate.color)? That sounds more likely, as static Foundry things go.
david aka claudekennilol
I don't suppose I can do something like bind:value={updates.data.measureTemplate.color || currentUserColor}?
TyphonJS (Michael)
Oh you'll like this.. I believe an empty string evaluates as false with Javascript.
david aka claudekennilol
Is there any kind log helper I can drop into my template?
Vauxs
Vauxs6mo ago
{@debug updates.data.measureTemplate.color} You can also be cheeky and drop a console log in the middle
david aka claudekennilol
Can I concatenate that with something?
Vauxs
Vauxs6mo ago
{console.log("lol") ?? ""}
david aka claudekennilol
Oh gosh
Vauxs
Vauxs6mo ago
Its just JS with the output being turned into HTML You can do anything as long as you dont mind the output being JS Or you do that trick I shown in case your code returns undefined
david aka claudekennilol
So anything between curly braces is just js? And the framework just treats it differently if the first char is a hashtag?
Vauxs
Vauxs6mo ago
Hashtag is a prefix for special functions But {} is indeed just JS anywhere you want
david aka claudekennilol
Dang, I had no idea
Vauxs
Vauxs6mo ago
like how you have
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
above updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha] could very well be fetch(something) or () => { return "the lolz" }
david aka claudekennilol
🤯
Vauxs
Vauxs6mo ago
ok maybe not the last one now I test it out, but generally thats the concept Intricacies apply™️
david aka claudekennilol
What about (() => 'value')() (i.e. wrapping the function in parens before executing it
Vauxs
Vauxs6mo ago
yeeeeah that does it
Vauxs
Vauxs6mo ago
No description
david aka claudekennilol
Well that doesn't help me at all 😅. Still cool though
Vauxs
Vauxs6mo ago
lol, you can do inline logs and stuff with that logic as said, this above lets you just output to console without any changes to HTML If you dont fancy using {@debug}
david aka claudekennilol
This is my else block as of right now
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
placeholder={currentUserColor}
data-edit="measureTemplate.color"
/>
<input
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
placeholder={currentUserColor}
data-edit="measureTemplate.color"
/>
If I erase the value, it should show "Player Color" which it does for a second (see gif). But then it saves that value still shows Custom Color.
No description
david aka claudekennilol
Sorry "save" is the wrong word. It doesn't save the color
Vauxs
Vauxs6mo ago
Best I can say is the whole data-edit thing is screwing you over, I really have no clue whats going on here
david aka claudekennilol
Man, even without data-edit it's still the same behavior
Vauxs
Vauxs6mo ago
Then I am pretty certain that bind:value causes an immediate update to some default value
david aka claudekennilol
{console.log("color", updates.data.measureTemplate.color)}
{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
{console.log("color", updates.data.measureTemplate.color)}
{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
I just don't understand how it's getting into this if block right here
No description
david aka claudekennilol
Nowhere else is using that label so it should jump to the else Oh wait, that's showing the return value of the log function
Vauxs
Vauxs6mo ago
yes
TyphonJS (Michael)
Undefined is false. so is empty string: https://developer.mozilla.org/en-US/docs/Glossary/Falsy
Vauxs
Vauxs6mo ago
You still have to look at the logs to see the console logs :wokeJoy:
david aka claudekennilol
Right, I'm running into lack of knowledge with the framework, not basic js knowledge
Vauxs
Vauxs6mo ago
We know Check the console and see what results you are getting
david aka claudekennilol
Yeah, the update value is flip-flopping. blank, when I erase it, but then immediately set to the player value instead of remaining empty
Vauxs
Vauxs6mo ago
I would just check for if the value === player color if I were you
david aka claudekennilol
I don't want to save the player color, though. Because if the player color changes, then I'll have an invalid color saved. I only want a color value saved if it was specifically set
Vauxs
Vauxs6mo ago
I still don't get why you need an entire if else statement here though Why not a single input that is empty with a placeholder user color and only change the label in presence of a value
david aka claudekennilol
Previously there was more in here so it made more sense to have all of the inputs swapped. But recent changes in the system negated all of that. I think I found what's going on and I'll try it with just a placeholder now. It looks like I was defaulting the color the player color when fetching my update options (because previously it made sense..but now it doesn't)
david aka claudekennilol
Hm, slight problem, it doesn't look like the placeholder color actually changes the color of the input. That should be purple.
{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
{:else}
<label for="defaultColor">{localizeFull("PLAYERS.PlayerColor")}</label>
{/if}
<div class="form-fields">
<input
bind:value={updates.data.measureTemplate.color}
id="colorOverride"
name="measureTemplate.color"
placeholder={currentUserColor}
type="text"
/>
<div class="color-input-border">
<input
bind:value={updates.data.measureTemplate.color}
data-edit="measureTemplate.color"
placeholder={currentUserColor}
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
/>
</div>
</div>
{#if updates.data.measureTemplate.color}
<label for="colorOverride">{localizeFull("PF1.CustomColor")}</label>
{:else}
<label for="defaultColor">{localizeFull("PLAYERS.PlayerColor")}</label>
{/if}
<div class="form-fields">
<input
bind:value={updates.data.measureTemplate.color}
id="colorOverride"
name="measureTemplate.color"
placeholder={currentUserColor}
type="text"
/>
<div class="color-input-border">
<input
bind:value={updates.data.measureTemplate.color}
data-edit="measureTemplate.color"
placeholder={currentUserColor}
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
/>
</div>
</div>
No description
Vauxs
Vauxs6mo ago
Yeah it doesnt, you will have to make a disabled, non-binded input if you want to preview it without making changes But that's easy enough, here the {#if else} actually helps Meanwhile the one at the top is somewhat superficial Just replace the localize
<label for="defaultColor">{localizeFull(updates.data.measureTemplate.color ? "PF1.CustomColor" : "PLAYERS.PlayerColor")}</label>
<label for="defaultColor">{localizeFull(updates.data.measureTemplate.color ? "PF1.CustomColor" : "PLAYERS.PlayerColor")}</label>
david aka claudekennilol
It needs to remain enabled so the color can be changed, though
Vauxs
Vauxs6mo ago
Then best I can say is deal with it or make an unlock button, locked by default it previews the user color, unlock and now the user can edit it but its black as expected Since input type="color" is pretty barren in terms of attributes
david aka claudekennilol
Can I wrap individual property assignments in {#if} blocsk?
Vauxs
Vauxs6mo ago
elaborate
david aka claudekennilol
Something like
{#if updates.data.measureTemplate.color}
bind:value={updates.data.measureTemplate.color}
{/if}
{#if updates.data.measureTemplate.color}
bind:value={updates.data.measureTemplate.color}
{/if}
Vauxs
Vauxs6mo ago
No treat hashtags as special HTML components you cannot put them inside of other components Hooooooooweeeever You can go around this problem unbind the value and use on:change instead You have to manually bind the changes to the update object but now you have control over it, including ignoring it if its the same as the player color
david aka claudekennilol
Ah, that's a good idea. Should be pretty simple 👍
david aka claudekennilol
Ok, thank you both for your patience with me. It's all working how I envisioned it now
<label for="colorOverride">
{localizeFull(updates.data.measureTemplate.color ? "PF1.CustomColor" : "PLAYERS.PlayerColor")}
</label>
<div class="form-fields">
<input
bind:value={updates.data.measureTemplate.color}
id="colorOverride"
name="measureTemplate.color"
placeholder={currentUserColor}
type="text"
/>
<div class="color-input-border">
<input
on:change={colorChanged}
data-edit="measureTemplate.color"
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
value={currentShownColor}
/>
</div>
</div>
<label for="colorOverride">
{localizeFull(updates.data.measureTemplate.color ? "PF1.CustomColor" : "PLAYERS.PlayerColor")}
</label>
<div class="form-fields">
<input
bind:value={updates.data.measureTemplate.color}
id="colorOverride"
name="measureTemplate.color"
placeholder={currentUserColor}
type="text"
/>
<div class="color-input-border">
<input
on:change={colorChanged}
data-edit="measureTemplate.color"
style="opacity: {updates.data.flags[MODULE_NAME][CONSTS.flags.colorAlpha]}"
type="color"
value={currentShownColor}
/>
</div>
</div>
$: {
currentShownColor = updates.data.measureTemplate.color || currentUserColor;
}

const colorChanged = (e) => {
const color = e.target.value;
if (color === currentUserColor) {
return;
}

updates.data.measureTemplate.color = color;
};
$: {
currentShownColor = updates.data.measureTemplate.color || currentUserColor;
}

const colorChanged = (e) => {
const color = e.target.value;
if (color === currentUserColor) {
return;
}

updates.data.measureTemplate.color = color;
};
No description
TyphonJS (Michael)
Excellent.. Thanks @Vauxs for jumping in and helping out! 😄 Unrelatred I did notice the localizeFull function. There is a helper function in TRL to use the core i18n localization. import { localize } from '#runtime/svelte/helper';
david aka claudekennilol
I'm not sure what the one you provide does, but I use mine because I've also wrapped format with it
const localizeFull = (key, opts = {}) =>
isEmptyObject(opts)
? game.i18n.localize(key)
: game.i18n.format(key, opts);
const localizeFull = (key, opts = {}) =>
isEmptyObject(opts)
? game.i18n.localize(key)
: game.i18n.format(key, opts);
In new mods (i.e. ones I work on regularly instead of just keep working), I use this instead so I can be more lazy 😅
const localize = (key, opts = {}) => {
const myKey = `${MODULE_NAME}.${key}`;
return isEmptyObject(opts)
? (game.i18n.localize(myKey) === myKey ? game.i18n.localize(key) : game.i18n.localize(myKey))
: (game.i18n.format(myKey, opts) === myKey ? game.i18n.format(key, opts) : game.i18n.format(myKey, opts));
}
const localize = (key, opts = {}) => {
const myKey = `${MODULE_NAME}.${key}`;
return isEmptyObject(opts)
? (game.i18n.localize(myKey) === myKey ? game.i18n.localize(key) : game.i18n.localize(myKey))
: (game.i18n.format(myKey, opts) === myKey ? game.i18n.format(key, opts) : game.i18n.format(myKey, opts));
}
TyphonJS (Michael)
There is format support... See docs localize('thing', { data object })
david aka claudekennilol
That's good. I'm not sure why Foundry has it split into two methods
Want results from more Discord servers?
Add your server