Giter VIP home page Giter VIP logo

felte's Introduction

Welcome!

My name is Pablo Berganza, I am a software developer, mostly working as a full-stack web developer. My main programming language is JavaScript, usually using TypeScript.

I am currently maintaining two self-made open source projects:

  • felte: A form management library for Svelte, Solid and React.
  • svelte-markdown: A markdown renderer that renders to Svelte components.
  • uvu-expect: An assertion library with a similar syntax to ChaiJS aimed to be used with uvu.
  • uvu-expect-dom: A plugin for uvu-expect that adds assertions for the DOM. Basically a wrapper on top of @testing-library/jest-dom.

Feel free to check them out!

My main programming languages

  • TypeScript
  • JavaScript
  • Rust
  • Clojure(Script)

Main technologies I work with

  • ReactJS
  • Svelte
  • NodeJS
  • NestJS

Links

felte's People

Contributors

avimar avatar basaran avatar benbender avatar bradlewis avatar chmac avatar dawidmachon avatar dependabot[bot] avatar djhi avatar ealush avatar ecelustka avatar eltociear avatar hmaesta avatar icalvin102 avatar jasongitmail avatar jfreyheit avatar koichikiyokawa avatar loremaps avatar pablo-abc avatar pasqui23 avatar rschristian avatar schurhammer avatar sirbyalive avatar timowilhelm avatar uhon avatar umanghome avatar winston0410 avatar xvenge00 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

felte's Issues

Reporter DOM should not require the id to be set.

Currently this works:

<input type="text" name="email" id="email" />
<div data-felte-reporter-dom-for="email" aria-live="polite" />

This does not:

<input type="text" name="email" id="email" />
<div data-felte-reporter-dom-for="email" aria-live="polite" />

Here's the relevant code of Reporter DOM:

  if (!target.name || !target.id) return;
  const validationMessage = target.dataset.felteValidationMessage;
  const reporterElement = document.querySelector(
    `[data-felte-reporter-dom-for=${target.id}]`
  );

I think we should not need to assign ids to every form element.

Error when expanding content (input elements) dynamically

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch @felte/[email protected] for the project I'm working on.

We are rendering a custom tree view inside a form. The tree nodes are expandable and contain input fields (checkboxes) with custom logic. When expanding the children of a node (with the corresponding checkboxes) the following error occured:

Uncaught Error: Function called outside component initialization

Here is the diff that solved my problem:

diff --git a/node_modules/@felte/reporter-svelte/src/reporter.js b/node_modules/@felte/reporter-svelte/src/reporter.js
index 8c7aa8f..82b2aa7 100644
--- a/node_modules/@felte/reporter-svelte/src/reporter.js
+++ b/node_modules/@felte/reporter-svelte/src/reporter.js
@@ -7,7 +7,6 @@ import { getPath, _get } from '@felte/common';
  * @param {any} currentForm
  */
 export function svelteReporter(currentForm) {
-  if (!hasContext(formKey)) setContext(formKey, currentForm.errors);
   if (!currentForm.form) return;
   const unsubscribe = currentForm.errors.subscribe(($errors) => {
     for (const control of currentForm.controls) {

This issue body was partially generated by patch-package.

Any reason why there is no validator-cvapi?

Hey,

just played with felte and had to ask myself the question if there is any reason I don't see, why there is no validator-cvapi?
Imho it could be lightweight validator for simple forms (like login f.e.) with built-in i18n. Most of the shortcomings of the cvapi are already handled by felte (premature validation, a11y-problems with the bubbles on mobile etc). So, am I overlooking something important or is it just not written yet? :)

Also while at it, I had a quick look on the api and noticed that the form itself isn't passed to the validate()-function - which would make the implementation of such an validator quite a bit harder. Therefor, additional question, any reasons for that? Maybe it could be a nice addition for extensibility to pass the form as a second arg I think!

And, last but not least: Thanks again for this great, well built, library! 💯

values passed to onSubmit are not cast by validator

Using yup to validate a form which has numbers, results in the values passed to onSubmit to not be numbers, but instead have their raw string value.

Using await schema.validate(values) or schema.cast(values) returns the values with the correct type, but should this not be the default?

validator-zod async support

thank you for the great library, I was wondering if there are any examples or if it is actually supported to use promise based schemas with validator-zod?

checkboxes

Hello, I ran into a corner and I'm probably doing something wrong. I have a form like most people, and I have checkboxes in them:

const {.....} = createForm({
    initialValues: initialValues,
    extend: [validatorYup, reporterDom({ single: true })],
    validateSchema: signupSchema,
});

Initial values:

export const initialValues = {
    fullName: "Michael Jackson",
    email: "[email protected]",
    password: "a",
    phoneNumber: "888-888-8888",
    agreeTos: true,
    agreeOptin: true,
};

and the form fields for checkboxes

                <div>
                    <label>
                        <input
                            aria-describedby="agreeTos-validation"
                            type="checkbox"
                            bind:checked={data["agreeTos"]}
                            name="agreeTos" /> Agree not to sue us
                    </label>
                    <div
                        id="agreeTos-validation"
                        data-felte-reporter-dom-for="agreeTos" />
                </div>

                <div>
                    <label>
                        <input
                            aria-describedby="agreeOptin-validation"
                            type="checkbox"
                            bind:checked={data["agreeOptin"]}
                            name="agreeOptin" /> Agree to receive spam
                    </label>
                    <div
                        id="agreeOptin-validation"
                        data-felte-reporter-dom-for="agreeOptin" />
                </div>

and this what happens after the initial load, unchecking one of the boxes, sets both of them to false.

felte-box

After clicking a few times around, it finally syncs things properly. I found this older issue thread #51

and following your setField advice takes care of the problem, you also had mentioned in the same post that this was not required any more.

If you could please give me some light, I will surely appreciate it.

P.S Other than the setField approach, if I bind to initialValues[fieldname] checkboxes also behave good. It's only when I bind them to $data.

Use [email protected] as peer dep

Could we use zod ^3.0.0 as the peer dep?

When running npm i -D zod, it installs [email protected]. I tested ^3.0.0 as the peer dep and it works.

Currently, I hit an error due to version mismatch:

Found: [email protected]
npm ERR! node_modules/zod
npm ERR!   dev zod@"^3.0.0-alpha.33" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer zod@"^1.11.13" from @felte/[email protected]

(I also noticed my vite error was resolved by removing zod and re-install using npm i -D zod as a devDependency. So we might want to update https://felte.dev/docs#using-zod)

Feature request: Change generic constraint for `createForm`

Current state

When providing an interface without the index signature (example below), the TypeScript compiler fails on createForm with the following error:

Type 'LoginRequest' does not satisfy the constraint 'Record<string, unknown>'.
  Index signature is missing in type 'LoginRequest'.

The LoginRequest itself looks like this.

export interface LoginRequest {
	email?: string;
	password?: string;
}

Code that throws the error:

const { form, data, errors } = createForm<LoginRequest>({
	onSubmit: (values) => console.log(values.email, values.password),
});

I could add the index accessor to the interface (they are generated based on our API), but this would reduce the type safety when using keyof or similar expressions in TypeScript.

Expected state

The interface should be usable without compiler errors. svelte-forms-lib solves this by defining a default value for the type constraint, see https://github.com/tjinauyeung/svelte-forms-lib/blob/master/lib/index.d.ts#L6-L12.

[request] Add HTMLFormElement as second argument to onSubmit()

At the moment, the only argument to onSubmit() is the Data entered to the form as a simple object.
I would like to be able to do something like the following to abstract the onSubmit-handling in different contexts:

const { form } = createForm<FormData>({
    onSubmit: async (values, form) => {
        const formData = new FormData();

        for (const key of Object.keys(values)) {
            formData.append(key, values[key]);
        }

        const result = await fetch(form.action, { // <-- dynamic use of form.action
   	    method: form.method,  // <-- dynamic use of form.method
            headers: {
                accept: 'application/json'
            },
            body: formData
        }).then((res) => res.json());

    // ...
    }
});

Therefor it would be great, if the form-object could be the second argument to onSubmit().

Proposal a way to handle formAction

Very nice job on felte! it's such an interesting form framework, and i love the approach of essentially defining everything from the HTML. I was thinking about how we usually handle the same form being used for multiple purposes in plain html, and it's done using the formAction attribute on the submit input, that way you can have several different submit buttons posting the form to different places.

I was thinking felte would be perfect for implementing a way to do this as well.

We could add another property data-felte-set-on-submit to input fields, that would only include the value of that field, if that corresponding submit button was used to submit the form here's an example of how the html could look:
https://svelte.dev/repl/099c65345fe043a79774c480d3b44070?version=3.34.0

The onSubmit function could then handle different submit functions based on which property was used to submit the from.

How do you feel about something like this? if it's something you think would be interesting i'd be happy to give a PR a shot.

Set `isSubmitting` to `false` again automatically, if validation errors exist.

I disabled my submit button like this, to prevent users clicking it twice while an HTTP POST is in flight.

<button type="submit" disabled={$isSubmitting}>submit</button>

But found a problem: if a client side error message exists, it's never re-enabled.

It should be re-enabled as soon as the error messages are displayed, so user can click submit again after fixing their errors.

Improve extender API

While the current extender API works, it feels quite hacky. A major thing that should be available is to modify or extend the Felte's configuration which currently relies on modifying the configuration object taking advantage of its mutability.

TypeError: Cannot read property '$$' of undefined

I started to get this error lately:

index.mjs?v=35e80513:1779 Uncaught (in promise) TypeError: Cannot read property '$$' of undefined
    at init (index.mjs?v=35e80513:1779)
    at new Form_1 (form.svelte? [sm]:5)
    at createProxiedComponent (svelte-hooks.js:245)
    at new ProxyComponent (proxy.js:239)
    at new Proxy<Form> (proxy.js:339)
    at create_if_block_2 (root.svelte? [sm]:38)
    at Array.create_default_slot (root.svelte? [sm]:37)
    at create_slot (index.mjs?v=09c160b6:69)
    at create_fragment (__layout.svelte? [sm]:48)
    at init (index.mjs?v=09c160b6:1799)

I've removed almost all of my code but it didn't fix the problem. So I'm getting this error with this code:

const {form, setField} = createForm({
    initialValues: {
      name: '',
      email: '',
      agreement: false,
      sex: "man"
    },
    onSubmit: values => {
      console.log(JSON.stringify(values))
    }
  });

TS error with extend

Hello, I'm really happy with the package and it works like intended – Thank You 👍. There is only problem that I have.
I use typescript and I get an TS-Error that I can't figure out how to solve.

Screenshot 2021-10-24 at 09 41 14

I tried different variations like extend: reporter() but with no luck. I hopefully attached everything necessary to fix this. Thank you for your help :)

Versions

"dependencies": {
		"@felte/reporter-tippy": "^0.3.9",
		"@felte/validator-yup": "^0.2.9",
		"felte": "^0.7.13",
               ...

My Code

<script lang="ts">
	import { createForm } from 'felte';
	import { validateSchema } from '@felte/validator-yup';
	import reporter from '@felte/reporter-tippy';
	import * as yup from 'yup';

	const schema = yup.object({
		email: yup.string().email().required()
	});

	const initialValues = {
		email
	};

	const { form, isValid } = createForm({
		initialValues,
		onSubmit: async ({ email }) => resetPassword(email),
		validate: validateSchema(schema),
		extend: [reporter()],
		onError: (errors) => log.error(errors)
	});

	async function resetPassword(email: string) {
                // reset pw stuff
	}
</script>

<form use:form>
	<label>
		E-Main
		<input name="email" type="email" />
	</label>
	<input type="submit" disabled={!$isValid} value="Send Reset Link" />
</form>

TypeScript Error Message

No overload matches this call.
  Overload 1 of 2, '(config: FormConfigWithInitialValues<{ email: string; }> & { initialValues: { email: string; }; onSubmit: ({ email }: { email: string; }) => Promise<void>; validate: ValidationFunction<...>; extend: Extender<...>[]; onError: (errors: unknown) => void; }): Form<...>', gave the following error.
    Type 'Extender<{ email: string; }>[]' is not assignable to type '(Extender<{ email: string; }> | Extender<{ email: string; }>[]) & Extender<{ email: string; }>[]'.
      Type 'Extender<{ email: string; }>[]' is not assignable to type 'Extender<{ email: string; }>[] & Extender<{ email: string; }>[]'.
        Type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/common/dist/types-1616e685").Extender<{ email: string; }>[]' is not assignable to type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/core/node_modules/@felte/common/dist/types-50c3e825").Extender<{ email: string; }>[]'.
          Type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/common/dist/types-1616e685").Extender<{ email: string; }>' is not assignable to type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/core/node_modules/@felte/common/dist/types-50c3e825").Extender<{ email: string; }>'.
            Types of parameters 'currentForm' and 'currentForm' are incompatible.
              Property 'addValidator' is missing in type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/core/node_modules/@felte/common/dist/types-50c3e825").CurrentForm<{ email: string; }>' but required in type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/common/dist/types-1616e685").CurrentForm<{ email: string; }>'.
  Overload 2 of 2, '(config: FormConfigWithoutInitialValues<{ email: string; }> & { initialValues: { email: string; }; onSubmit: ({ email }: { email: string; }) => Promise<void>; validate: ValidationFunction<...>; extend: Extender<...>[]; onError: (errors: unknown) => void; }): Form<...>', gave the following error.
    Type 'Extender<{ email: string; }>[]' is not assignable to type '(Extender<{ email: string; }> | Extender<{ email: string; }>[]) & Extender<{ email: string; }>[]'.
      Type 'Extender<{ email: string; }>[]' is not assignable to type 'Extender<{ email: string; }>[] & Extender<{ email: string; }>[]'.
        Type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/common/dist/types-1616e685").Extender<{ email: string; }>[]' is not assignable to type 'import("/Users/jonasschell/Programming/neesh_repos/laptops_in_space_repos/monorepo/node_modules/@felte/core/node_modules/@felte/common/dist/types-50c3e825").Extender<{ email: string; }>[]'.ts(2769)
(property) FormConfigWithoutInitialValues<{ email: string; }>.extend?: Extender<{
    email: string;
}> | Extender<{
    email: string;
}>[]
Optional function/s to extend Felte's functionality.

Bug - is not validating correctly when destroying/recreating input

Editing for the use of email to better understand, but basically the validation is lost when it destroys / recreates the input

  • I have two inputs. Each one shows according to a previous selection.
  • If user selects Paypal, I need email-paypal
  • If user selects Payoneer, I need email-payoneer`
  • Each input is only shown if needed. We don't show email-paypal if user selected Payoneer
  • Both inputs are linked to email variable – so, if user types in one input, it will be applied to the another one

The problem is:

  1. Select Paypal
  2. Enter an invalid value in email-paypal
  3. See the error message
  4. Select Payoneer
  5. Type a valid value in email-payoneer
  6. Select Paypal again
  7. Note that now the input value is VALID, but the error message is still showing up
  8. Click on Submit – it won't submit, saying that the email-paypal is invalid, but it's not
  9. Type a new character in email-paypal
  10. The error message will disappear and you can submit

Basically, when an input is destroyed, it's reference continues in $error store and when it is created again it's already showing as "invalid".

Example: https://codesandbox.io/s/bug-validate-if-felte-forked-b0kgj?file=/App.svelte:192-197
Video: https://user-images.githubusercontent.com/15247903/115600317-f7311700-a2b2-11eb-88f5-d4c31479afd7.mp4

Felte not reacting to checkbox's `checked` property

Hello, first of all, what an amazingly useful library this is! It has made my life SO much better with Svelte when creating forms. I did want to point out a potential bug I have encountered and it goes like this:

Consider the following:

<script>
  /* init form stuff */

  let checked = false
</script>

<form use:form>
  <input name="myInput" {checked} />

  <button on:click={() => checked = !checked}>Toggle Checkbox Above</button>
</form>

If I happen to directly click on the checkbox, then the form data is updated as expected, however, if I click the button instead, the form data is not updated.

multistep reporter displays errors on steps 2+ and select

Hello,

This originates from @outerlook's multistep demo at #25

I did not want to hijack his issue, so I made a new one.

I'm having some issues using Felte reporters with multi-step forms.

1) Steps 2+ display errors by default

If you look at his REPL at: https://svelte.dev/repl/d83a688976fd466ab0a1d55400ab94f6?version=3.38.2

He's using a dynamic component to load the steps. If you complete step 1(email), step 2 has the error message displayed by default(password requirements...) since submit has already been used.

2) If you use a select, it displays errors by default(before submit)

See this fork REPL at: https://svelte.dev/repl/f92f4e8afa3247cfb1e7ff4113437028?version=3.38.2

I tried this with Tippy reporter as well, but it doesn't want to play nice with the svelte REPL, so I stuck with the dom reporter.

Also tried swapping out for the yup validator, but saw the same issue.

Is this the "best practices" approach towards a multistep form?

I'm not sure if this needs to be worked on at the validator or reporter level, or if it's just a feature that needs more development.

Any guidance on how to use the felte validators/reporters on a multi-step form would be greatly appreciated.

Thanks!
Ryan

p.s. Here's another approach with custom prev/next buttons and functional pre-population: https://github.com/MirrorBytes/MultiStep -- @MirrorBytes actually suggested I take a look at Felte instead since you had put in a lot of refinement and gained traction.

Here's hoping for a good way to pull it all together and have a great forms experience in Svelte.

t2.addValidator is not a function

Hello,

I did a cleanup on my node_modules and did a fresh yarn install on the latest svelte. Compiler is throwing as follows:

proxy.js:19 TypeError: t2.addValidator is not a function
    at n (index.ts:42)
    at index.mjs:1
    at Array.map (<anonymous>)
    at stores (index.mjs:1)
    at w (index.mjs:1)
    at instance (SignupForm.svelte:380)
    at init (index.mjs:1791)
    at new SignupForm (SignupForm.svelte:363)
    at createComponent (svelte-hooks.js:136)
    at SignupForm.targetCmp.$replace (svelte-hooks.js:183)
  vite v2.5.10 dev server running at:

  > Local:    http://localhost:4000/

  SvelteKit v1.0.0-next.174

I think it's the @felte/validator-yup somehow not working anymore. I will keep looking.

reporter-tippy error: 500 Cannot destructure property 'controls' of 'i' as it is undefined.

When using Tippy to display validation errors, the page shows a 500 area when loading a page containing the form: 500 Cannot destructure property 'controls' of 'i' as it is undefined..

If I pass {single:true} to the reporter method, this becomes 500 t is not a function.

<script>
  import { createForm } from 'felte';
  import reporter from '@felte/reporter-tippy';
  import * as yup from 'yup';

  const schema = yup.object().shape({
    email: yup.string().email().required(),
    message: yup.string().required()
  });

  const { form } = createForm({
    extend: reporter(),
    onSubmit: (values) => {
      console.log(values);
    },
    validate: (values) => {
      try {
        schema.validate(values);
      } catch (err) {
        return err;
      }
    }
  });
</script>

<form use:form>
  <label for="email">Email:</label>
  <input id="email" name="email" aria-describedby="email-validation" />
  <div id="email-validation" aria-live="polite" />

  <label for="message">Message</label>
  <input type="text" name="message" aria-describedby="message-validation" />
  <div id="message-validation" aria-live="polite" />

  <button type="submit">submit</button>
</form>

Function for submitting the form programatically?

Hi, I want to submit the form in an event listener, but I cannot find any function for that in the api doc.

https://api.felte.dev/

I have then tried out the traditional form.submit() (and reference the <form> element with bind:this) in Javascript, but seems like Felte is not catching the submit there.

const handleKeydown = (e: KeyboardEvent) => {
    if(e.code === "Enter"){
        formElem.submit()
    }
}

Did I missed something in the documentation? Or is this function not there yet? I would love to help with the doc or the code.

Minor Feature Request

Smooth scroll to the error field, instead of an abrupt one.

It's just a nice to have, not entirely necessary, but will fit well with wherever I use svelte-scrollto.

bug: Function called outside component initialization

I was attempting to setup a CodeSandBox to demonstrate another problem I was having.

While setting up the sandbox I am getting an error that I cannot get past....

"Function called outside component initialization"

CodeSandBox Error...
image

I do actually get this error locally but somehow SvelteKit is able to get past it and the form still works but CodeSandBox cannot. I get an error for each input on the page.

image

Here is my CodeSandBox. I am sure it something that I am misconfiguring due to my mediocre Svelte skills. 🤔

CodeSandBox

unexpected behaviour of validation when using createSubmitHandler

Description

For a form we have to provide two different submit actions that perform different tasks and also differ in validation. To achieve that, we use the createSubmitHandler helper.

Please have a look at this simplified reproduction for further insight: https://svelte.dev/repl/b9f7503bbc5b4e838fcbe2bce4e9bbd9?version=3.34.0

You will see a form with two simple text inputs and two submit buttons. The standard submit validates only the first input field. The second submit uses the same validation but additionally should check the validity of the second input field.

Expected Behavior

if first text input has no value:

  • do not perform submit for standard submit or second submit
  • give visual error feedback in UI for first text input

if first input has a value and second input has no value:

  • perform submit for standard submit but not for second submit
  • give visual error feedback in UI for second text input when using second submit

Actual Behavior

if first text input has no value:

  • does not perform submit for standard submit or second submit
  • gives visual error feedback in UI for first text input

if first input has a value and second input has no value:

  • performs submit for standard submit but not for second submit
  • no visual error feedback in UI for second text input when using second submit

Possible Solution

Instead of just returning the appropriate errors object in the second validation handler, additionally i can use setError to achieve our goals.

	const secondSubmit = createSubmitHandler({
		validate: (values) => {		
			const errors = {}

			if (!values.second) {
				errors.second = 'required'
                                 setError('second', 'required')
			}

			return errors
		},
		onSubmit: (values) => {
			console.log('second submit', values);
		}
	})

Maybe I got something wrong , but i would expect felte to handle that case.

Thanks for your efforts.

Tippy is positioned at the top left corner for `display: none` elements.

Hey @pablo-abc,

I'm using a library called Slim Select for Select drop down. It's an advanced select drop down component.

It hides the original <select> element, which causes the tippy error reporter to show error message at the top left of the screen.

Screenshot from 2021-04-03 22-20-24

Is it possible to specify the container, perhaps the label, of a select element as tippy anchor point?

What should be the best course of action?

P.S. Also the tippy doesn't show up on directly hovering over the Slim Select, instead I had to hover over the label to see the error.

`reset()` should not trigger validation errors

After submitting a form and receiving the response, I call reset(). It clears the input values as expected, but it also triggers all inputs to show their validation errors & shouldn't.

(I'm using DOM reporter, yup, & have required values for all inputs in my form as part of the yup validation.)

 if (res.status === 200) {
    reset();
 }

initialValues is not working

I noticed one issue, the initial Values is not working as expected.

Screenshot from 2021-03-31 14-15-04

Praise

Thanks for making such a wonderful library. (I don't have to combine svelte-forms-lib with svelte-tippyjs and svelte-scroll, just to achieve what you have done!).
Felte solves all the problems I wanted a form library to solve. For example svelte-forms-lib:

  1. Doesn't auto focus on error field.
  2. Has old school way of error message support, unlike using Tippy.js.
  3. Too verbose, as I have to include onChange on every field and values as well.

P.S.

I am going to write a blog post about this library, at the end of this week, on my blog Derpy Coder
And I am using this library in my project Quiller Bee, repo Quiller Bee - Dgraph Branch

Error importing yup: `TypeError: Line must be greater than or equal to 1, got -1`

Sorry. I don't know what changed, maybe Svelte Kit did. But even importing yup (without using it or using a reporter) causes the build to fail now.

Error:

TypeError: Line must be greater than or equal to 1, got -1
    at BasicSourceMapConsumer.SourceMapConsumer_findMapping [as _findMapping] (/Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:67770:13)
    at BasicSourceMapConsumer.SourceMapConsumer_originalPositionFor [as originalPositionFor] (/Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:67839:22)
    at /Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:68799:34
    at String.replace (<anonymous>)
    at /Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:68789:21
    at Array.map (<anonymous>)
    at ssrRewriteStacktrace (/Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:68788:10)
    at Object.ssrFixStacktrace (/Users/me/my-app/node_modules/vite/dist/node/chunks/dep-efe32886.js:69043:27)
    at Object.get_stack (file:///Users/me/my-app/node_modules/@sveltejs/kit/dist/chunks/index.js:3359:28)
    at get_response (file:///Users/me/my-app/node_modules/@sveltejs/kit/dist/ssr.js:2272:32)

Repro. Comment & un-comment the yup import to see it work or break:

<script context="module">
  import { createForm } from 'felte';
  import reporterDom from '@felte/reporter-dom';
  // import { validateSchema } from '@felte/validator-yup';
  // import * as yup from 'yup'; //  << this line

  // const schema = yup.object({
  //   email: yup.string().email().required(),
  //   message: yup.string().required()
  // });

  const { form } = createForm({
    extend: reporterDom(),
    onSubmit: (values) => {
      console.log('values', values);
    }
    // validate: validateSchema(schema)
  });
</script>

<h1>Contact</h1>

<form use:form>
  <label for="email">Email</label>
  <input
    id="email"
    type="text"
    name="email"
    aria-describedby="email-validation"
  />
  <div
    id="email-validation"
    data-felte-reporter-dom-for="email"
    aria-live="polite"
  />

  <label for="message">Message</label>
  <input
    id="message"
    type="text"
    name="message"
    aria-describedby="message-validation"
  />
  <div
    id="message-validation"
    data-felte-reporter-dom-for="message"
    aria-live="polite"
  />

  <button type="submit">submit</button>
</form>

package.json

	"devDependencies": {
		"@sveltejs/adapter-node": "next",
		"@sveltejs/kit": "next",
		"autoprefixer": "^10.2.5",
		"cssnano": "^4.1.10",
		"postcss": "^8.2.7",
		"postcss-load-config": "^3.0.1",
		"svelte": "^3.29.0",
		"svelte-preprocess": "^4.6.9",
		"vite": "^2.1.0"
	},
	"type": "module",
	"engines": {
		"node": ">= 12.17.0"
	},
	"dependencies": {
		"@felte/reporter-dom": "^0.1.9",
		"@felte/validator-yup": "^0.1.0",
		"felte": "^0.5.3",
		"yup": "^0.32.9"
	}

on change events fire unintentionally

Hello again again :)

I have these initial values:

    multipleSelect: null,
    stateSelect: null,
    fruit: null,

and these are attached to three inputs. I think due to the way the change events are being monitored, this is what happens for instance, when I change the plain select fruit.

events

Wouldn't the expected behavior be that only the fruit element gets marked? Like this one:

input

and I did check, in none of these cases, the form is not submitting. But the $errors get updated like this in one shot:

image

Code for fruit is as follows:

        <div>
            <label for="fruit" aria-invalid={$errors["fruit"]}>Fruit</label>
            <select id="fruit" name="fruit" bind:value={$data["fruit"]}>
                <option value="" selected>Select a fruit…</option>
                <option>…</option>
            </select>
        </div>

Be able to setup a field level validation schema

Description

Hi! It would be nice to set some field level validation.

Example

My example is a multi-step form that shares the same

element. This form asks for phone, name and email.

HTML
 STEP 1
<form>
	<input name='name'>
	<button type='submit'/>
</form>

 STEP 2
<form>
	<input name='phone'>
	<button type='submit'/>
</form>

STEP 3
<form>
	<input name='email'>
	<button type='submit'/>
</form>

I use zod for validating things.

And I'm using the following schema:

### Schema
const validationSchema = z.object({
	name: z.string().nonempty(),
	phone: phoneRegexValidation,
	email: emailValidation
})

Example's issue

Every step is required. But I can't use like that. If all of them are required for submitting, I can't use the button as a submit button for controlling my form. And I do have some clear options:

  • Handle only the last button as submittable, control my "steps" out of onSubmit (a bit boilerplatey)
  • Separate into 3 forms, one with each validation (less svelte composable components power)

There is an option I used on react, using the library react-hook-form

  • dynamically setup my validationSchema when "step" changed
### Schema
const validationSchema = step => z.object({
	name: z.string().nonempty(),
	phone: step > 0 ? phoneRegexValidation : optional,
	email: ztep > 1 ? emailValidation : optional
})
### Creating form context
const { form } = createFormContext({
		validateSchema: validationSchema(step),
		onSubmit: values => {
			if (!isLastStep) step++;
			else submitToServer(values)
		}
	})

But it doesn't seems to work (it could be another issue if I did it right, but that's not the main request here)

Svelte-action

I've being trying to create an svelte action to solve that:

### Implementation
export const validate = (
	node: HTMLInputElement,
	{ schema }: { schema: z.Schema<any> }
) => {
	const listener = (ev: Event) => {
		try {
			schema.parse(node.value)
		} catch (e) {
			ev.preventDefault();
			ev.stopPropagation();
			ev.stopImmediatePropagation();
			const error = e as z.ZodError<any>
			node.setCustomValidity(error.message)
		}
	}
	node.form?.addEventListener('submit', listener)

	return {
		destroy: () => {
			node.form?.removeEventListener('submit', listener)
		}
	}
}
### Usage
	<input
		use:validate={nameValidation}
		name="name"
	/>

But I'm having no success on that. It isn't preventing form onSubmit from being executed. I don't know how to achieve that outside felte.

Further comments

The library I mentioned (react-hook-form) uses a different and less html native for subscribing new fields that can validate at this level.

RHF validation docs

I mention them only for bringing examples of libraries that have this kind of field-level API (and are choosing to continue supporting it).

Another benefit of solving it would be composability problem. A form would not need to know every field that are inside them for validating purposes.

dom-reporter input focus

Hello again :)

It appears the dom reporter is looking for data-felte-validation-message.

The issue I'm having with this now it took two days of my life component is that, even I can inject the attribute data-felte-validation-message into it, it becomes out of sync, so when the dom-reporter is doing the querySelect on the form, it can't find it at that time.

I tested the theory with importing tick().then(() => { ...focus }) into the dom-reporter, it was partially sucessful. setTimeout was the most successful.

I'm just asking so I learn, what do you think about having the dom-reporter accept an onSubmitError callback in the options and if it is not provided then execute the standard logic?

[Question] Best way to build progressive forms with felte?

As pictured in the default example of sveltekit, we have the perfect preconditions to build progressive forms with svelte which will work even without JS and progressively enhance with that. I quite like this feature and would like to use it with felte.

Problem is, that, if I add method="post" and action="..." to my felte-enhanced form, it will post the form with js loaded. It seems that felte doesn't do the .preventDefault().

Ideally I would like to see that usecase supported first class in felte, but if that's out of/not your scope, I'm a bit unsure what the best way would be to achive it? As far as I could see, I could overwrite the onSubmit-handler but that seem to be a lot of boilerplate for the simple intercept...

Disable felte handlers on custom components

I have a custom component that wraps a textbox. On change, the value is transformed, then it should update felte's data store.

Because felte automatically binds the textbox's events/value, there is no way for me to transform this value.

Is there a way to disable felte's event handlers on a certain element and manually wire up setField?

Edit: a data attribute to prevent this would be awesome!

Arrays in `intialValues` field causes issues in `errors` and `touched` stores

Hi, I have a use case where the initial data has an array of fields for data. You could think of it like items on a shopping list. However, when I attempt to implement it the field in the errors store becomes null instead of an array of nulls and in touched its set to true instead of an array of falses.

I've tried to convert the array to an object with integer indexes as keys, which works. The errors and touched show the correct object structure but they do not seem to update (errors does not get the error messages returned from the validation function and touched does not change the fields to true when changed). It also causes some issues with validating.

See example: https://codesandbox.io/s/felte-demo-forked-s2fdq

When initial data has object, felte generates wrong errors

I have schema where one field contains an object:

const schema = yup.object({
    name: yup.string().required(),
    select: yup
      .object()
      .nullable()
      .default(null)
      .required("Required")
  });

When you pass initialValues to createForm function it generates an error: null for every field of the object:

const { form, errors } = createForm({
  initialValues: {
    name: "Test",
    select: { value: "test", label: "Test" }
  }
});

errors in this case:

{"name":null,"select":{"value":null,"label":null}}

but it should be

{"name": null, "select": null}

Here is the sandbox with this example:
https://codesandbox.io/s/felte-validation-problem-0dbbm?file=/App.svelte:154-222

Verbose Validation Error Message Shown

I have the following schema:

let validateSchema = yup.object().shape({
    title: yup.string().trim().required("Title Required!"),
    url: yup
        .string()
        .url("URL must be Proper!")
        .required("Job URL is Required!"),
    salary: yup.object({
        min: yup
            .number("Salary must be a number!")
            .positive("Salary must be greater than 0!")
            .integer("Salary must be an Integer!"),
        max: yup
            .number("Salary must be a number!")
            .positive("Salary must be greater than 0!")
            .integer("Salary must be an Integer!")
            .moreThan(
                yup.ref("min"),
                "Max Salary must be larger than Minimum!"
            ),
        currency: yup.string().required(),
    }),
});

However the message shown is not the one I specified:

Screenshot from 2021-04-01 08-18-48

missing input name throws

Hi, I don't mean to take too much of your time, but wanted to let you know about another edge case I ran into and what I did to bypass it somewhat.

There is this nice custom component called svelte-select.

Normally, this component doesn't use a named input field, as the input field is only used to filter the select options, and it gives you an on:select handler. So, if you use this with use:form, you get this:

index.ts:149 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'focus')
    at Object.onSubmitError (index.ts:149)
    at index.mjs:1
    at Array.forEach (<anonymous>)
    at HTMLButtonElement.<anonymous> (index.mjs:1)

I fixed it with a fork of the package and using a hidden named input, and tested various scenarios including transforms. It wasn't pleasant but it was still easier than writing a component from scratch. Just wanted to let you know of the error encountered.

recaptcha

Hello, would it be consistent with the purpose of felte to add a few extra event listeners like some sort of hooks. I have two different use cases.

  1. Captcha preprocessing
  2. Invoking animations on 3rd party UI libraries.

captcha preprocessing:

At the moment, I have attached the recaptcha execution to the onSubmit handler, I also understand I could have done this the other way around, call the recaptcha first and then captchaOnSuccess, call createForm submitButtonHandler. But I figured it would be better to initiate captcha the last in case the token expires or something.

    onSubmit: async (values) => {
        debug("launching recaptcha");
        await recaptcha.execute();
    } /*
       │at this point all validations have passed and it
       │is okay to launch captcha sequence. rest of the
       │data submission will proceed after captcha
       │succeeds and emits onSuccess event.
       */,

createform -> validations -> onSubmit -> execute captha function -> onCaptchaSuccess -> doPost the form inside the callback ----> do server side validation return json -> use setError to update the form.

This is working fine, but it's a bit of flow jumping. Especially with the second round the form is submitted if there are server side validation errors. You see, for instance if I setError(phoneNumber) because it is invalid on the server side (although it looks valid syntax wise), it disrupts my use of validate and $isSubmitting and $isValid in my validate() function, which I use to display some toasts in an effort to assist the user with a visual cue.

    validate: (values) => {
        if ($isSubmitting && !$isValid) {
            debug("launching custom validation");
            showToast({
                title: "Incomplete Request",
                description: "Please check your inputs",
                type: "warning",
                showProgress: false,
            });
        }
    } /*
       │this is custom validation logic, we basically
       │use it to show some toast notification as a form
       │of user feedback. All validations actually
       │happen through yup validator.
       */,

when setError() is done on the phoneNumber , it would still launch the toast, and proceed with the server post. I tried setTouch but not sure how that works. I couldn't find a way to reset the errors, the reset function appear to be resetting everything wiping out the user entered form data.

So, I think it could be helpful, if there were some beforeSubmit, afterSubmit, beforeValidate, aterValidate hooks, and some way to clear error states so it could be possible to trigger some css animations. Otherwise, user might feel like nothing is happening when they press the submit button (that's the reason why I put the toast there like that).

P.S the closest resetError I could do was:

for (const [k, v] of Object.entries(values)) {
            setError(k, false);
}

inside the validate(). But because the validate executes several times during init, and every time the during focus change, it animates the whole form. I was able to work around this by using $isSubmitting however.

felte.mp4

zod > yup?

To check the production build size consequence of using yup (with DOM reporter), I built 3 ways:

Felte with no validation:
.svelte/output/client/_app/pages/contact.svelte-1a0155d9.js        27.72kb / brotli: 8.37kb

Felte + yup & @felte/validator-yup imported:
.svelte/output/client/_app/pages/contact.svelte-f4df8f52.js          77.59kb / brotli: 20.89kb

Felte + only @felte/validator-yup imported (to check size):
.svelte/output/client/_app/pages/contact.svelte-ccc585dd.js         59.51kb / brotli: 16.80kb

Yup adds >200% to Felte's size. After exploring bundlephobia, I found zod.

  • 50% smaller than Yup
  • v3 Zod is tree shakeable
  • contains URL, email, regex, etc too
z.string().min(5);
z.string().max(5);
z.string().length(5);
z.string().email();
z.string().url();
z.string().uuid();
z.string().regex(regex);
z.string().nonempty();

What do you think about zod?

@felte/reporter-dom: Validation errors not displayed, but are console logged.

With the DOM reporter, I can submit the form data successfully. But I can't get it to show an error message if the validation failed--it shows no error in the DOM nodes for the validation error messages, but it logs to the browser console: Uncaught (in promise) ValidationError: email must be a valid email

<script>
  import { createForm } from 'felte';
  import reporterDom from '@felte/reporter-dom';
  import reporter from '@felte/reporter-tippy';
  import * as yup from 'yup';

  const schema = yup.object().shape({
    email: yup.string().email().required(),
    message: yup.string().required()
  });

  const { form } = createForm({
    extend: reporterDom(),
    onSubmit: (values) => {
      console.log('values', values);
    },
    validate: (values) => {
      try {
        schema.validate(values);
      } catch (err) {
        return err;
      }
    }
  });
</script>

<form use:form>
  <label for="email">Email</label>
  <input
    id="email"
    type="text"
    name="email"
    aria-describedby="email-validation"
  />
  <div
    id="email-validation"
    data-felte-reporter-dom-for="email"
    aria-live="polite"
  />

  <label for="message">Message</label>
  <input
    id="message"
    type="text"
    name="message"
    aria-describedby="message-validation"
  />
  <div
    id="message-validation"
    data-felte-reporter-dom-for="message"
    aria-live="polite"
  />

  <button type="submit">submit</button>
</form>

[Feature Request] - HMR Store with Local Storage Support

Hey @pablo-abc,

I have a feature request, HMR & Local Storage:

  1. With HMR, working with forms in Vite will be crazy fast.
  2. With Local Storage support, the form data will become impervious to data loss in case of page refresh.

Is it prudent to add HMR Store & Local Storage support to felte's stores?

I took the following HMR Safe Store code from Vite init.

// Customized HMR-safe stores
// Based off https://github.com/svitejs/svite/blob/ddec6b9/packages/playground/hmr/src/stores/hmr-stores.js
import { writable } from "svelte/store";

/**
 * @type { Record<string, import('svelte/store').Writable<any>> }
 */
let stores = {};

/**
 * @template T
 * @param { string } id
 * @param { T } initialValue
 * @returns { import('svelte/store').Writable<T> }
 */
export function getStore(id, initialValue) {
	return stores[id] || (stores[id] = writable(initialValue));
}

// preserve the store across HMR updates
if (import.meta.hot) {
	if (import.meta.hot.data.stores) {
		stores = import.meta.hot.data.stores;
	}
	import.meta.hot.accept();
	import.meta.hot.dispose(() => {
		import.meta.hot.data.stores = stores;
	});
}

P.S. I won't mind a workaround instead.

Trigger form submit programatically?

Excellent form lib!

Would it be possible to trigger a form submit event from an element outside the form?

Use case is if a form is used on a modal dialog, the submit button may be in the modal footer and outside the form element itself.

I'm thinking something like this, but I don't know if it's possible to get a reference to the form:

<button type="submit" bind:this={aRefToTheForm?}>submit from outside the form</button>

[Bug] esm-import of @felte/common fails when using @sveltekit/adapter-node

Just noticed that there is a bug with @felte/common when building a sveltekit-app using @sveltejs/adapter-node@next. The problem disappears if I remove the adapter. The problem doesn't exist when doing a named import from f.e. the felte-package itself...

I'm unsure why this happens as the correct esm-package seems to be built and is referenced in @felte/common's package.json... There seem to be a subtle difference in the rollup-config which makes the adapter unhappy ;)

vite v2.4.4 building SSR bundle for production...
✓ 74 modules transformed.
.svelte-kit/output/server/app.js   199.25kb

Run npm run preview to preview your production build locally.

> Using @sveltejs/adapter-node
> Named export 'getPath' not found. The requested module '@felte/common' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@felte/common';
const { getPath } = pkg;

file:///[..]/.svelte-kit/output/server/app.js:2
import { getPath } from "@felte/common";
         ^^^^^^^
SyntaxError: Named export 'getPath' not found. The requested module '@felte/common' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@felte/common';
const { getPath } = pkg;

Reproduction

  1. npm init svelte@next felte-esm-repro && cd felte-esm-repro && npm i && npm add -D @felte/common @sveltejs/adapter-node@next
  2. Use the default for every question by hitting enter.
  3. Add the following to your svelte.config.js:
import node from '@sveltejs/adapter-node';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	kit: {
  	adapter: node(),
		// hydrate the <div id="svelte"> element in src/app.html
		target: '#svelte'
	}
};

export default config;
  1. Add the following to src/routes/index.svelte:
<script>
	import { getPath } from "@felte/common";
	import Counter from '$lib/Counter.svelte';

	console.log(getPath);
</script>
  1. run npm run build and see it fail.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.