Open fudom opened 3 months ago
This might be the same request as https://github.com/ionic-team/ionic-framework/issues/20521 (just with checkbox instead of toggle)
It restricts a lot of features like text-wrap and more complex labels like sub-label etc.
You can still do this with the new syntax. Check out the playground demo on https://ionicframework.com/docs/api/item#text (this example uses a toggle with wrapping/subtext, but it works the same way with checkboxes).
Also I wouldn’t recommend the usage you are proposing as it creates a nested interactive. A nested interactive is a bad practice that makes your app less accessible because screen readers don’t always know which interactive to focus. In this case there is a checkbox inside of a button.
If you want to indirectly control the checkbox using another button in the item I’d try putting the checkbox in the start slot and putting the toggle button in the default slot.
In Ionic 6 I used pointer-events: none
for the toggle/checkbox inside an item. This allowed me to toggle it with a click on ion-item and prevents nested interactive action.
With Ionic 8 (I skipped 7), this works out of the box which is great. Now you can click on item to toggle the value without hacks (additional changes). But this prevents me from using the item click to toggle the value anymore. If I do so, it toggles twice which appears as not changed. You know what I mean? I use the click event to have more control over how to toggle. And the toggle/checkbox just indicates the actual state. Use case: On item click > try to enable something > may keep it disabled.
I think implementing readonly
(like text inputs have), will fix this issue. It should behave like disabled
without opacity changes. It appears with regular styles. But also used pointer-events none to prevent nested actions. This is my current workaround. I use the disabled
attribute to mute it and an additional css selector to reset the opacity back to 1. I think this would be the best and easiest way. Just officially supported by Ionic with the readonly
attribute.
Alternative: $event.preventDefault/stopPropagation
on item. But how can Ionic detect this? It's more effort and more complex for the dev to work with than just adding readonly attribute.
Just a demo of my current solution:
<ion-item button (click)="toggle()">
<ion-label class="ion-text-wrap">
<h2>Toggle me</h2>
<p>Toggles the value if possible.</p>
</ion-label>
<ion-toggle slot="end" readonly disabled [checked]="value"></ion-toggle>
</ion-item>
// Appears as readonly. Can be removed when Ionic officially supports it.
[readonly][disabled] {
opacity: 1;
}
// Adds a gap to text.
.in-item[slot='end'] {
margin-left: 20px;
}
The toggle is not used as input directly. It's more like an icon, an indicator. It cannot be changed directly. Therefore I used the layout like from Ionic 6 with item slots. But I could also use the new label style. It does not really matter for this issue/case. It's about readonly input.
If I do so, it toggles twice which appears as not change
Inside of ion-toggle
is a label
. When a click event is dispatched on that element (by the user clicking) it will dispatch another click event on any descendant inputs. Both of these click events bubble up across the shadow dom boundary. You can filter out the duplicate click event by ignoring ones with pointerId: -1
The toggle is not used as input directly. It's more like an icon, an indicator. It cannot be changed directly. Therefore I used the layout like from Ionic 6 with item slots. But I could also use the new label style. It does not really matter for this issue/case. It's about readonly input.
I'm not entirely sure why you are wanting to do this as it goes against both iOS and Android design principles. Even if you removed the pointer events from the toggle screen readers would still read it as a toggle instead of an icon which could confused users.
Also your current solution is confusing because screen readers will inform users that the toggle is not interactive because it is disabled. However, clicking on the item will still update the toggle.
I'd strongly recommend following the patterns shown on https://ionicframework.com/docs/api/item#content-types. These patterns align with how list items in iOS and Android behave. By aligning with this patterns you can ensure that you are providing an experience that is familiar to a majority of your users.
I don't really care about screen readers. It's a Cordova/Capacitor app always in md mode. In general, I agree with you. But I don't have time for major changes. How would you migrate this? Especially the click event which does not always toggle the value. For demo lets say: this.value = Math.random() > 0.5
. I could use [(ngModel)]
with get
and set
. The get
replaces [checked]
and set
replaces (click)
. But is more effort to implement. At the moment I am under time pressure to migrate the app. But I will take a closer look at other solutions you mentioned when I get the chance. My solution with readonly works good for now.
Screen readers exist on mobile devices (VoiceOver on iOS and TalkBack on Android) and can be used with Capacitor/Cordova apps.
Doing something like this will give you an experience that is accessible and familiar to most users: https://codepen.io/liamdebeasi/pen/pomWpZr
Prerequisites
Describe the Feature Request
I updated Ionic from 6 (to 7) to 8 and have problems with the breaking changes of the input components. Feedback: I'm not sure if the new label attribute solution is better than the ion-label before. It restricts a lot of features like text-wrap and more complex labels like sub-label etc. If I use the legacy ion-label, it blocks the click event and you cannot toggle the checkbox anymore... I'm sure you had good reasons for that, but it feels like one step forward and two steps backward. Anyway...
The reason for this ticket is the following problem:
The new behavior of ion-item with an input element like e.g. ion-toggle is now different:
What's the problem? Well, it's ok. But you can't control the ion-toggle (or checkbox whatever) indirectly anymore. Let me show an use case.
Describe the Use Case
The code above is a migration from Ionic 6 to 8. I use the new label style. Well, looks good. But the problem is, that (click) on item changes the toggle value. In this case, I don't want to change the value directly. Therefore I added
pointer-event: none
to it. Works great in Ionic 6. Ionic 8 does not care about. Because ion-items handles it now.Background: The function
toggleValue
on click calls and changes thevalue
if possible. But now the bindingchecked
is ignored and the toggle is always toggles. Or toggles twice if the value is toggles indirectly when clicked.Describe Preferred Solution
I want disconnect the input element like
ion-toggle
orion-checkbox
fromion-item
to use the click event without affecting the input element. LikestopPropagation
orpreventDefault
which I already tried. Maybe add an attributemute
orpassive
to the input? Btw.readonly
does also not work anddisabled
would change the appearence and would the click event would not work.In other words, in this case: The ion-toggle etc should only be used as indicator. A dummy only for showing the current state. It should not be controlled directly by click. Only indirectly by item click event. We could implement
readonly
which is the same as disabled but appears regular (not grayed out).My current workaround: Use
disabled
and a class name to set the opacity back to 1. Fortunately, the click event of ion-item still works. It seems to work as expected. So readonly attribute may fix this issue.