Skip to content

Fieldset object depth issue #6

@axelra82

Description

@axelra82

I'm getting an issue where the fields get "linked" (writing in on field updates all of them, except the initial fieldset) when adding a new fieldset (i.e. formHandler.addFieldset({ basePath: "contacts" })).

Ex.

  • Initial contact #1 (not affected by subsequent fieldsets)
  • Add new contact #2 and fill in all required fields, then create another contact (#3)
  • Last contact #3 will be added with the data from fields name.first and name.last in contact #2
  • Changing values in contact #2 or #3 will change in both, contact #1 is not affected

An obvious an easy fix (in this scenario) is to just change the schema (see below) so the names (first and last) aren't in a new object level, i.e. firstName and lastName. But for other cases this might not be optimal, e.g. someDeepObjectLevelStructureOne (...two, etc) which maybe should have been some.deep.object.level.structure.one.

ZOD schema excerpt:

const schema = z.object({
	...
	contacts: z.array(
		z.object({
			name: z.object({
				first: z.string().min(2, "Minimum 2 characters"),
				last: z.string().min(2, "Minimum 2 characters"),
			}),
			email: z.string().email("Incorrect contact email"),
			phone: z.string().min(5, "Minimum 5 characters").optional().or(z.literal("")),
			password: z.string().min(8, "Minimum 8 characters").optional().or(z.literal("")),
			createUser: z.boolean().default(false),
		}),
	).min(1, "There must be at least one (1) contact person for a client"),
});

Usage/Code excerpt:

<h3 class="h2">Contacts</h3>
<HorizontalDivider margin={4} />
<section>
	<ul>
		<For each={formHandler.formData().contacts}>
			{(contact, index) => (
				<li
					class={cn(
						"relative mb-8 grid grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-x-8 sm:gap-y-0",
						{
							"mt-4 border-t border-t-gray-200 pt-8": index() > 0,
						},
					)}
				>
					<div class="col-span-2 flex w-full items-center justify-end gap-x-2">
						{formHandler.formData().contacts.length > 1 && (
							<div class="text-sm text-gray-400">
								# {index() + 1}
							</div>
						)}
						{index() > 0 && (
							<Button
								iconOnly
								round
								flush
								leadingIcon={Icons.CANCEL}
								leadingIconSize={Size.SM}
								color={Colors.DESTRUCTIVE}
								{...(!isWorking() && {
									onClick: () => formHandler.removeFieldset(index(), "contacts"),
								})}
							/>
						)}
					</div>
					<FormField
						label="First name"
						placeholder={index() % 2 === 0 ? "John" : "Jane"}
						id={`contacts.${index()}.name.first`}
						name={`contacts.${index()}.name.first`}
					/>
					<FormField
						label="Last name"
						placeholder="Doe"
						id={`contacts.${index()}.name.last`}
						name={`contacts.${index()}.name.last`}
					/>
					<FormField
						label="email"
						placeholder={`${index() % 2 === 0 ? "john" : "jane"}.doe@email.com`}
						id={`contacts.${index()}.email`}
						name={`contacts.${index()}.email`}
					/>
					<FormField
						label="Phone (optional)"
						id={`contacts.${index()}.phone`}
						name={`contacts.${index()}.phone`}
					/>
					<Show when={contact.createUser}>
						<FormField
							label="password"
							name={`contacts.${index()}.password`}
							type={InputType.PASSWORD}
							toggleShowPassword
							required
							autocomplete="password"
						/>
					</Show>
					<FormCheckbox
						onClick={() => {
							// eslint-disable-next-line @typescript-eslint/no-floating-promises
							formHandler.setFieldValue(`contacts.${index()}.createUser`, !`contacts.${index()}.createUser`);
							if (contact.createUser) {
								// eslint-disable-next-line @typescript-eslint/no-floating-promises
								formHandler.setFieldValue(`contacts.${index()}.createUser`, undefined);
							} else {
								// eslint-disable-next-line @typescript-eslint/no-floating-promises
								formHandler.setFieldValue(`contacts.${index()}.password`, generatePassword());
							}
						}}
						class="col-span-2"
						id={`contacts.${index()}.createUser`}
						name={`contacts.${index()}.createUser`}
						label="Create user account"
						hint="Select if you want to creat a user account, with the “owner” role, in the client for this contact"
					/>
				</li>
			)}
		</For>
	</ul>
	<Button
		leadingIcon={Icons.ADD}
		onClick={() => formHandler.addFieldset({ basePath: "contacts" })}
		disabled={isWorking() || !canAddUser()}
	>
		add contact
	</Button>
</section>

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingsolvedIssue solved.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions