alpinejs / alpine

A rugged, minimal framework for composing JavaScript behavior in your markup.
https://alpinejs.dev
MIT License
28.19k stars 1.23k forks source link

Binding radios with true/false #257

Closed kez closed 3 years ago

kez commented 4 years ago

I've been attempting to find some boolean database columns (via an API and fetch()) to 2 grouped radio buttons. One radio should represent 'true' and the other 'false'.

There seems to be a disconnect between the value on the radio which ends up as a string representation of the boolean versus the bound data which is a proper boolean per JSON.

I've put up a simple demo of the issue here (https://codepen.io/k-dobson/pen/WNvdGeB) - I would have expected all three of these to be set to checked, but you can see that only happens when the data model is set as strings from the x-model bind.

Not sure if this is a bug, per say, or something I've missed in the docs/examples. Any insight appreciated!

SimoTod commented 4 years ago

Hi @kez

The value of a radio button is always a string when you read it using javascript while the values in your x-data object are booleans. if you try 'true' == true in dev console, you can see that it evaluates to false.

At the moment, Alpine doesn't convert it for you. It supports the number modifier on x-model directives (although it's not documented) but we don't have a boolean modifiers yet.

If @calebporzio is okay with that, we can create a PR to add it and update the documentation.

In the meantime, you need to make sure that your values are strings if you can.

kez commented 4 years ago

Thanks @SimoTod - totally makes sense. Given the variable nature of the value field of radios or checkboxes (e.g. could be set to "on", "true") it makes sense to force the casting upstream somewhere. I've got a toBoolean helper function to satisfy various x-show directives which works fine with the mix of booleans and strings.

Another option could be an extension of the :attribute handlers to somehow allow the evaluation of a function to set the checked attribute (it's obviously not the same as a :class='abc xyz' because inputs are just looking for "checked". That way x-model could bind to the value and the checked status could be driven by a user function to handle the values directly.

(I've only been using Alpine for a week so forgive me if the above sounds like complete garbage).

I'll cast my booleans in the API response for now and keep an eye out in the future - happy to close this off.

SimoTod commented 4 years ago

If you don't need to keep the value bound to a model, you can already have something like

        <input :checked="radios.first + '' == 'true'" type="radio" value="true">First <br />

of add the relevant function to x-data and have something like

        <input :checked="ifFirstChecked()" type="radio" value="true">First <br />

or

        <input :checked="isChecked(radios.first, 'true')" type="radio" value="true">First <br />

or any other implementation you think it suits your existing code.

For those who need a the model, I don't see any harm in having something like x-model.boolean but it's just a personal opinion, let's see what other people think.

benfurfie commented 4 years ago

I knew I'd solved this problem previously for another framework. I just couldn't remember what it was. Took me most of the day, but then on seeing @kez's example, I remembered that you can (ab)use the JSON.parse method to force cast a string to a bool.

It won't solve this problem 100 per cent, but by doing something like JSON.parse(radios.first), you could ensure that the value being returned is a bool and not a string.

HugoDF commented 4 years ago

@benfurfie nice one, I think there's a couple of workarounds

question is now, should we add a .boolean modifier for x-model? (which would literally just apply Boolean(value))

I think it would be cool for someone to add a PR with it, worst case @calebporzio doesn't merge it 😄

benfurfie commented 4 years ago

Interestingly, I tried Boolean, but it didn't like it. I'll have to take a look at what's involved, as I've never looked at the core of Alpine. 👍