pablo-abc / felte

An extensible form library for Svelte, Solid and React
https://felte.dev
MIT License
1.01k stars 43 forks source link

Feature request: `getField`/`getFields`-methods #84

Closed taffit closed 2 years ago

taffit commented 2 years ago

As much as I love the organization of hierarchical forms/properties, some things become more difficult. E. g. writing a method that references the value using bracket annotation with the complete path (e. g. field["mygroup.myfield1"] doesn't resolve, so you need some own methods to resolve the path. As we have something similar that sets a values where you can provided the path/nested object (setField/setFields), the counterpart for reading would be helpful.

For others having the same problem: I'm using the following implementations:

// Flattens an object, e. g. {a: {b: 1}} => {a.b: 1}
// https://stackoverflow.com/a/66683974/1785391
const flattenObject = (obj) => {
    return Object.entries(obj).reduce((acc, [key, value]) => {
        if (typeof value === 'object' && value !== null) {
            Object.entries(value).forEach(([iKey, iValue]) => {
                acc[`${key}.${iKey}`] = iValue;
            });
        } else {
            acc[key] = value;
        }
        return acc;
    }, {});
};
// Gets the value from an object with the path provided, e. g.
// let o = {a: {b: 1}}; resolvePath(o, "a.b") => 1
// https://stackoverflow.com/a/43849204/1785391
const resolvePath = (object, path, defaultValue) =>
    path
        .split(/[\.\[\]\'\"]/)
        .filter((p) => p)
        .reduce((o, p) => (o ? o[p] : defaultValue), object);
// Sets a value within a (nested) object on the path provided
// https://stackoverflow.com/a/43849204/1785391
const setPath = (object, path, value) =>
    path
        .split('.')
        .reduce((o, p, i) => (o[p] = path.split('.').length === ++i ? value : o[p] || {}), object);
pablo-abc commented 2 years ago

I'll be working on this today. We're using the last two internally (a getField of sorts), so exposing them should not be an issue. How would a getFields be different to reading the whole data store as $data?

taffit commented 2 years ago

Maybe I'm too confused about the $data-store in combination with the transformations. The $data-store contains all the values as displayed in the form fields, if I understood it correctly. So these can be strings, numbers, formatted, having input masks etc. The transformations will transform these values to get the raw data, e. g. from a $data.myNumber="1.000,00" (string) to 1000.00 (decimal). I thought that a getFields()-method could return the raw/transformed data.
Re-reading the documentation about the transformations, I understood, that these already transform the values to the raw data. So I just misunderstood the usage: I initially thought, that these will transform the initialValues (where I set the raw data until now) to be displayed in a formatted way.

Tl;dr: I guess the getFields()-method isn't needed as, as you said, we already have the $data-store with the raw data using the transformations.

pablo-abc commented 2 years ago

Felte by default tries to do the same transforms Svelte would do. E.g. a type=number input will be parsed into a number. Besides that it doesn't do much. So you can safely assume that what the data store contains is the same value a bind:value would give you.

Of course this is no longer the case if you add transforms.


On another note, I've merged the getField function with master. I'll be pushing a new version after I do some other changes.

taffit commented 2 years ago

E.g. a type=number input will be parsed into a number

If you add input masks to the game, this will no longer be true, as every input-field will be of type="text". In this/my case I need to do this transformation manually from the formatted number to the raw number. No problem, though, as I know now what can be found in which store.

Thank you for the quick implementation and clarification, @pablo-abc !

pablo-abc commented 2 years ago

@taffit getField should be available already in the latest version of Felte! As well as setInitialValues and isDirty from #83

pablo-abc commented 2 years ago

@taffit preparing for 1.0.0 and after some testing on production I've opted to remove this helper in order to favour a more generic one. Starting on 1.0.0 a utility getValue will be exported from felte (instead of being returned by createForm). Its usage will be something like:

// Within a .svelte <script>
const email = getValue($data, 'email')

My reasoning is that, using getField we need to manually obtain the current value of the store, while with getValue we know you already have the current value of the store (in Svelte via $data, $errors, etc).

You can keep track of any other breaking changes in #90. There is a migration guide already, of course. It'll be published when 1.0.0 is released. But you can also see the preview of them in #90 as well.


I'll close this since this is technically solved already in the latest versions as well with getField.