diff --git a/core/src/components/radio-group/radio-group.tsx b/core/src/components/radio-group/radio-group.tsx
index 37e0f0da0c9..4659d4e1d7e 100644
--- a/core/src/components/radio-group/radio-group.tsx
+++ b/core/src/components/radio-group/radio-group.tsx
@@ -1,6 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Listen, Method, Prop, State, Watch, h } from '@stencil/core';
-import { checkInvalidState } from '@utils/forms';
+import { checkInvalidState, isOptionSelected } from '@utils/forms';
import { renderHiddenInput } from '@utils/helpers';
import { getIonMode } from '../../global/ionic-global';
@@ -104,7 +104,7 @@ export class RadioGroup implements ComponentInterface {
// Get the first radio that is not disabled and the checked one
const first = radios.find((radio) => !radio.disabled);
- const checked = radios.find((radio) => radio.value === value && !radio.disabled);
+ const checked = radios.find((radio) => isOptionSelected(value, radio.value, this.compareWith) && !radio.disabled);
if (!first && !checked) {
return;
@@ -219,7 +219,8 @@ export class RadioGroup implements ComponentInterface {
if (selectedRadio && !selectedRadio.disabled) {
const currentValue = this.value;
const newValue = selectedRadio.value;
- if (newValue !== currentValue) {
+ const isSelected = isOptionSelected(currentValue, newValue, this.compareWith);
+ if (!isSelected) {
this.value = newValue;
this.emitValueChange(ev);
} else if (this.allowEmptySelection) {
@@ -275,7 +276,8 @@ export class RadioGroup implements ComponentInterface {
// space bar on top of a selected radio
if ([' '].includes(ev.key)) {
const previousValue = this.value;
- this.value = this.allowEmptySelection && this.value !== undefined ? undefined : current.value;
+ const isSelected = isOptionSelected(this.value, current.value, this.compareWith);
+ this.value = this.allowEmptySelection && isSelected ? undefined : current.value;
if (previousValue !== this.value || this.allowEmptySelection) {
/**
* Value change should only be emitted if the value is different,
diff --git a/core/src/components/radio-group/test/radio-group.spec.tsx b/core/src/components/radio-group/test/radio-group.spec.tsx
index fdd6c644267..cd13bd2acb3 100644
--- a/core/src/components/radio-group/test/radio-group.spec.tsx
+++ b/core/src/components/radio-group/test/radio-group.spec.tsx
@@ -108,4 +108,43 @@ describe('ion-radio-group', () => {
expect(radios[2].getAttribute('aria-checked')).toBe('true');
expect(radioGroup.value).toEqual(3);
});
+
+ it('should allow deselection with compareWith when allowEmptySelection is true', async () => {
+ const page = await newSpecPage({
+ components: [Radio, RadioGroup],
+ template: () => (
+
+ Option #1
+ Option #2
+
+ ),
+ });
+
+ const radioGroup = page.body.querySelector('ion-radio-group')!;
+ const radios = document.querySelectorAll('ion-radio')!;
+
+ expect(radios[0].getAttribute('aria-checked')).toBe('true');
+
+ await radios[0].click();
+ await page.waitForChanges();
+
+ expect(radioGroup.value).toBeUndefined();
+ expect(radios[0].getAttribute('aria-checked')).toBe('false');
+ });
+
+ it('should handle null values safely when compareWith is a property name', async () => {
+ const page = await newSpecPage({
+ components: [Radio, RadioGroup],
+ template: () => (
+
+ Option #1
+ Option #2
+
+ ),
+ });
+
+ const radios = document.querySelectorAll('ion-radio')!;
+ expect(radios[0].getAttribute('aria-checked')).toBe('false');
+ expect(radios[1].getAttribute('aria-checked')).toBe('false');
+ });
});
diff --git a/core/src/utils/forms/compare-with-utils.ts b/core/src/utils/forms/compare-with-utils.ts
index 2ab1fd0237b..b342132920a 100644
--- a/core/src/utils/forms/compare-with-utils.ts
+++ b/core/src/utils/forms/compare-with-utils.ts
@@ -15,7 +15,7 @@ export const compareOptions = (
if (typeof compareWith === 'function') {
return compareWith(currentValue, compareValue);
} else if (typeof compareWith === 'string') {
- return currentValue[compareWith] === compareValue[compareWith];
+ return currentValue?.[compareWith] !== undefined && currentValue?.[compareWith] === compareValue?.[compareWith];
} else {
return Array.isArray(compareValue) ? compareValue.includes(currentValue) : currentValue === compareValue;
}
@@ -33,7 +33,7 @@ export const isOptionSelected = (
compareValue: any,
compareWith?: string | CompareFn | null
) => {
- if (currentValue === undefined) {
+ if (currentValue === undefined || currentValue === null) {
return false;
}
if (Array.isArray(currentValue)) {