colinaut / alpinejs-plugin-simple-validate

Simple Alpine form validation plugin
97 stars 4 forks source link

Extend x-validate.group to regular input fields #22

Closed Rocketpilot closed 7 months ago

Rocketpilot commented 7 months ago

This might be a bit of an edge case, but I was wondering about a validation rule for the following pattern:

  1. a set of inputs for personal phone number, work phone number, mobile phone number
  2. only one of which is required
  3. if none are chosen, the validation rule kicks in and an error message is displayed

I guess this is an extension of x-validate.group. I don't think I can use the same name attribute for each input as the user may add more than one phone number.

It's sort of almost working but it's not picking up the provided error message from the div I've wrapped it in.

colinaut commented 7 months ago

Interesting use case. Ive got some ideas on how to make it work. I’ll kick it around and get back to you.

colinaut commented 7 months ago

Figured out a simple work around for your issue. Just add a boolean :required that checks all the variables.

    <label for="phone-personal">Personal Phone</label>
    <input
        type="phone"
        id="phone-personal"
        name="phone-personal"
        :required="!($validate.value('phone-personal') || $validate.value('phone-cell') || $validate.value('phone-work'))"
    />

    <label for="phone-cell">Cell Phone</label>
    <input
        type="phone"
        id="phone-cell"
        name="phone-cell"
        :required="!($validate.value('phone-personal') || $validate.value('phone-cell') || $validate.value('phone-work'))"
    />

    <label for="phone-work">Work Phone</label>
    <input
        type="phone"
        id="phone-work"
        name="phone-work"
        :required="!($validate.value('phone-personal') || $validate.value('phone-cell') || $validate.value('phone-work'))"
    />
Rocketpilot commented 7 months ago

Thanks for that - I'll mull over it. I would say though, and this might well be beyond your plausible use-cases for the plugin, that one motivation for a built in approach is that for business security reasons we're required to avoid anything with unsafe-eval, so I'm migrating our so-far limited use of Alpine to the new Alpine-CSP build, which doesn't permit any inline javascript expressions.

colinaut commented 7 months ago

Makes sense. My plugin does lean on inline js but it can work without it with some work arounds. You can instead use Alpine.bind(). You will need to also add an Alpine.data() for the form or somewhere higher up in the DOM, even if it returns an empty {}, since Alpine requires data somewhere for plugins to work.

I tested the following code and it works. Note in the Alpine.bind() the use of explicit function instead of an arrow function. You need to do it this way to can access magic variables like my $validate off of the this

<form x-data="form" x-validate>
    <label for="phone-personal">Personal Phone</label>
    <input
        type="phone"
        id="phone-personal"
        name="phone-personal"
        x-bind="phones"
    />
    <label for="phone-cell">Cell Phone</label>
    <input
        type="phone"
        id="phone-cell"
        name="phone-cell"
        x-bind="phones"
    />
    <label for="phone-work">Work Phone</label>
    <input
        type="phone"
        id="phone-work"
        name="phone-work"
        x-bind="phones"
      />
</form>
<script>
    document.addEventListener("alpine:init", () => {
        Alpine.data("form", () => {
        return {};
        });
        Alpine.bind("phones", function () {
        return {
        ":required"() {
                    return !(
                        this.$validate.value("phone-personal") ||
                        this.$validate.value("phone-cell") ||
                        this.$validate.value("phone-work")
                    );
        },
        };
        });
        });
</script>
Rocketpilot commented 7 months ago

This is a very clear explanation and in fact helps me understand a lot of weird stuff about Alpine that the official docs don't really help with. I think it might be useful for other people struggling, too. Thank you very much!

colinaut commented 7 months ago

Yeah building a plugin definitely was a learning experience with Alpine. Maybe I should fire up my blog again and post about what I've learned regarding Alpine.