Aitor Sánchez - Blog - Oct. 27, 2023, 6:54 p.m.
¿Pensando en incluir radio buttons en tu app de Ionic? o, tal vez, solo quieres conocer más detalles sobre sus campos o funciones ¿verdad?
Mi nombre es Aitor Sánchez, soy desarrollador de apps desde 2014, y en artículo de hoy aprenderás cómo puedes incluir botones de selección en una aplicación hecha en Ionic.
Pero antes de continuar, esta es la Flutter Mafia. Es mi newsletter donde aprenderás desarrollo de apps móviles, aso y monetización junto con otros genietes que ya están dentro. Y si te suscribes te regalo mi ebook "Duplica los ingreso de tus apps en 5 minutos" No es broma, quizás te interese.
Pues para quien no lo sepa, se trata de los típicos botones redondos que se agrupan para seleccionar una opción u otra. Vamos a ver un ejemplo, en forma de demo, antes de continuar:
¿Y ahora? ¿Mejor? Seguro que sí.
Este componente no requiere instalación previa. Simplemente podemos usarlo sin problemas llamando a las etiquetas correspondientes. Sigue leyendo.
Yeahh! Yujuuuu ¿Qué haces Aitor? Pues nada compañero, simplemente que no requiere configuración tampoco jajajaj. Sigamos.
Que mejor opción para entender algo que ver un ejemplo de uso:
<ion-list radio-group [(ngModel)]="relationship">
<ion-item>
<ion-label>Friends</ion-label>
<ion-radio value="friends" checked></ion-radio>
</ion-item>
<ion-item>
<ion-label>Family</ion-label>
<ion-radio value="family"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Enemies</ion-label>
<ion-radio value="enemies" [disabled]="isDisabled"></ion-radio>
</ion-item>
</ion-list>
Vale, en primer lugar, tenemos que crear un ion-list llamando a la propiedad radio-group (ion radio group) para que la lista tengas este estilo. ¿Por qué? Ya sabes que Ionic trata todos los inputs como ion-item así que es necesario por esto.
Por otro lado, como puedes ver en el código, bindeamos con el campo relationship. En este campo se guardará el valor de lo que hemos seleccionado.
Continuamos creando un ion-item donde vamos a meter el label y el ion-radio para el que estamos trabajando. Esto es muy simple, lo único que hay que destacar es que el ion-radio tiene una etiqueta “value” que es la que se guardará en nuestra object antes mencionada.
Y listo, esta sería la estructura de uso para este tipo de componente. Tiene algo más de personalización, pero te lo dejo a tu criterio el seguir investigando.
Ahora vamos a ver qué tipo de datos y properties nos ofrece este componente cuando un usuario los usa.
En el caso de que queramos cambiar el texto (text) asociado, tendremos que cambiar el texto del label, no del value del componente.
Como he dicho al principio, este es un componente muy simple. Así que solo tiene un evento de salida.
Otra de las cuestiones principales que me habéis comentado es, que no sabéis cómo podéis bindear un ion radio a través de un ngModel. Pues la respuesta es muy sencilla y la vamos a ver en el siguiente ejemplo:
<ion-list radio-group [(ngModel)]="opcion">
<ion-list-header>
Unit
</ion-list-header>
<ion-item>
<ion-label>Opción 1</ion-label>
<ion-radio value="1"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 2</ion-label>
<ion-radio value="2" ></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 2</ion-label>
<ion-radio value="2" ></ion-radio>
</ion-item>
</ion-list>
Aunque ya hemos visto algo parecido en el código anterior, la opción más sencilla de hacer el binding es así. El ngModel se usará en el padre "ion-list" con el atributo "radio-group". Digamos que lo usaríamos del mismo modo que usamos un "select". Si pensabas que tenías que añadir el ngModel a los "ion-radio" aquí tienes la solución a tus problemas :)
En primer lugar, hay que comentar que este error es muy común en la actualidad. Y la base de esto es que en el cambio de versiones de v3 a v4 han cambiado varias cositas. Pero la solución es super sencilla. Basta con que cambiemos nuestro código anterior por algo así:
<ion-list>
<ion-radio-group [(ngModel)]="opcion">
<ion-list-header>
Auto Manufacturers
</ion-list-header>
<ion-item>
<ion-label>Opción 1</ion-label>
<ion-radio value="1"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 2</ion-label>
<ion-radio value="2"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 3</ion-label>
<ion-radio value="3"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 4</ion-label>
<ion-radio value="4"></ion-radio>
</ion-item>
<ion-item>
<ion-label>Opción 5</ion-label>
<ion-radio value="5"></ion-radio>
</ion-item>
</ion-radio-group>
</ion-list>
En esencia, se ha retirado el componente "radio-group" de la ecuación que se compartía con la etiqueta "ion-list" y ha aparecido como nueva etiqueta "ion-radio-group". Esta última será la encargada de montar todo el sistema de grupo para los radio buttons.
Así que ya sabes, el tema es que el atributo "radio-group" ya no funciona y ahora hay que declararlo así.
En esencia mucho me habéis comentado porque no funcionan los ion-radio en los formcontrol cuando lo extraes por el formcontrolname de un form normal y corriente. Después de probarlo me di cuenta de que es cierto, que no terminan de funcionar bien (depende de la versión de Ionic). Es posible que los reactive form de Angular tampoco funcionen por este motivo. Pues una posible solución que he encontrado es la siguiente:
Nota: este ejemplo también nos sirve para todos aquellos quieran bindear un ion-radio con un ngModel.
<ion-row radio-group formControlName="catchGender" [(ngModel)]="genero" [ngModelOptions]="{standalone: true}">
<ion-item no-lines>
<h2>Género</h2>
</ion-item>
<ion-col col-4>
<ion-item no-lines>
<ion-label>
<ion-icon name="male"></ion-icon> Hombre</ion-label>
<ion-radio value="male"></ion-radio>
</ion-item>
</ion-col>
<ion-col col-4>
<ion-item no-lines>
<ion-label>
<ion-icon name="female"></ion-icon> Mujer</ion-label>
<ion-radio value="female"></ion-radio>
</ion-item>
</ion-col>
<ion-col col-4>
<ion-item no-lines>
<ion-label>Unknown</ion-label>
<ion-radio value="unknown"></ion-radio>
</ion-item>
</ion-col>
</ion-row>
Por otro lado, este código también soluciona el problema "no value accessor for form control with name". He revisado por Github a ver si encontraba algo respecto al tema, y en efecto, esto lo soluciona. Lo chungo de esta excepción reside en el binding de los radios.
Cómo puedes ver, los encerramos en un "radio-group" y los bindeamos a la variable "genero". Así cuando usemos el submit, accedemos a la variable género y ahí estarán los datos. En caso de que el ion-radio este active, devolverá un par con, por ejemplo, {male: true}.
Continuamos el artículo hablando de cómo le podemos incluir imágenes a nuestros ion-radio. Directamente no lo podemos hacer, dado que no es permitido, pero aquí os voy a dejar una posible solución al problema. Espero que pueda ser de tu ayuda.
En primer lugar, aquí está el HTML de nuestros "ion-radio". Si te fijas son componentes HTML nativos. También podrías meterlos en un radiogroup si quisieras.
<form>
<div>
<input id="a1" type="radio" name="a" value="visa" />
<label for="a1">Visa</label><br/>
<input id="a2" type="radio" name="a" value="mastercard" />
<label for="a2">Mastercard</label>
</div>
</form>
Ahora veamos el style CSS necesario para poner las imágenes:
.cc-selector input{
margin:0;padding:0;
-webkit-appearance:none;
-moz-appearance:none;
appearance:none;
}
.visa{background-image:url(http://i.imgur.com/lXzJ1eB.png);}
.mastercard{background-image:url(http://i.imgur.com/SJbRQF7.png);}
.cc-selector input:active +.drinkcard-cc{opacity: .9;}
Pues listo, ya tenemos nuestros flamantes ion-radio box con las imágenes que queríamos. Es más, y para ir un poquito más allá, usando esto también podríamos cambiar / change el color del background del mismo que hemos puesto la imagen. En lugar de usar "background-image" usaríamos "background-color" o "background" a secas. Sigamos...
La siguiente parte que me habéis consultado en varias ocasiones es cómo podemos cambiar el color cuando este está seleccionado. Pues la verdad que es muy sencillo, pero ahí va:
<ion-radio color="secondary" checked></ion-radio>
En caso de que queramos algún color que no esté definido en los base de Ionic. Lo único que tenéis que hacer es ir a vuestra paleta de color base, donde están las variables de compilación de CSS, y crear un color nuevo y ponérselo.
He aprovechado este ejemplo también para mostrar cuando está checked el componente en sí. Así que si estás buscando cómo puedes ponerlo en checked, o un default checked, aquí lo tienes.
Decir que lo que estamos buscando, un onChange event (ngChange) como tal, no existe en nuestro componente. Pero porque no es necesario que exista, para tal fin vamos a usar el método "ionSelect" (selected event) que se disparará cuando se marque, o desmarque, nuestro componente. Un ejemplo sería el siguiente:
<ion-radio (ionSelect)="radioChecked()" ....>
Basta decir que cuando se marque la casilla se disparará el evento "radioChecked" no confundir con el evento ngChecked en este ejemplo. ¿Lo has entendido? De no ser así, dímelo en los comentarios y le damos un repaso más profundo.
Nota: Este ejemplo también aplicaría para un click event (onClick). Funcionaría exactamente igual.
Este punto es muy interesante, y por el que mucho habéis consultado, y es que tenéis una ligera dificultad en cómo podemos crear radios dinámicamente, por ejemplo, en un *ngFor. Pues bien, primero vamos a ver un ejemplo de código:
<ion-scroll scrollY="true">
<ion-item *ngFor="let user of listOfUsers" [(ngModel)]="selectedUser">
<ion-label>{{ user.title }}</ion-label>
<ion-radio [checked]="checkDefault(user)" value="{{ user.value }}" (ionSelect)="OnUserChanged(user)"></ion-radio>
</ion-item>
El objeto sobre el que realizamos la iteración tiene que tener una variable booleana que llamando a la function "checkDefault" nos diga si tenemos que checkearlo cómo pone en el ejemplo para hacer el preselect. Y listo, todo estaría funcionando a las mil y una maravillas :)
Bien, esto es un dilema que he tenido yo también alguna vez, no porque me lo haya preguntado un usuario. Pues bien, la consulta se asemejaría en cómo podemos hacer que los botones se vean de manera horizontal en lugar de vertical. Pues dándole vueltas, la mejor conclusión a la que he llegado es la siguiente:
<ion-radio-group [(ngModel)]="miVariable">
<ion-row>
<ion-col>
<ion-item>
<ion-label>A</ion-label>
<ion-radio mode="md" item-left value="A"></ion-radio>
</ion-item>
</ion-col>
<ion-col>
<ion-item>
<ion-label>B</ion-label>
<ion-radio mode="md" item-left value="B"></ion-radio>
</ion-item>
</ion-col>
<ion-col>
<ion-item>
<ion-label>C</ion-label>
<ion-radio mode="md" item-left value="C"></ion-radio>
</ion-item>
</ion-col>
</ion-row>
</ion-radio-group>
Cómo ves, lo único que hemos hecho ha sido llamar a la rejilla de ionic y crear una fila con 3 columnas. En sí mismas, ellas se reparten el espacio, no es necesario asignarles un valor de ancho (o width). Sobre el tema del alto (o height) no será necesario que hagamos nada tampoco. Será el mismo componente el que se encargue de ajustar todo en su sitio.
He recordado que una vez, en una app, usé unos radios super chulos, unos ion radio style button. Sobre estaban chulos para Android (para iOS desentonaban un poco, pero bueno) y he encontrado la manera de que lo puedas ver en el artículo. Aquí está:
Nota: Simplemente debéis obviar la parte de "JavaScript" porque son de Angular 1, así que eso no es necesario hacer nada con ello.
ion-content {
background-color: #fff;
padding:15px;
}
.radio .radio-icon {
visibility: visible !important;
}
.radio .radio-icon:before {
content: "" !important;
border: 2px solid black !important;
width: 24px !important;
height: 24px !important;
border-radius: 50% !important;
overflow: hidden !important;
}
.radio .radio-icon:after {
content: "" !important;
position: absolute !important;
right: 20px !important;
top: 22px !important;
background: black !important;
width: 12px !important;
height: 12px !important;
border-radius: 50% !important;
overflow: hidden !important;
transition: -webkit-transform .28s cubic-bezier(0.420, 0.000, 0.000, 1.650);
transition: transform .28s cubic-bezier(0.420, 0.000, 0.000, 1.650);
-webkit-transform: scale(0);
transform: scale(0);
}
.radio.item-radio > input[type=radio]:checked ~ .radio-icon:after {
-webkit-transform: scale(1);
transform: scale(1);
}
.radio .item-content {
background-color: #fff;
margin: 0;
padding-right: 50px;
padding-left: 0px;
}
.radio.item-radio > input[type=radio]:checked ~ .item-content {
background-color: #fff;
}
.radio-inline.item {
display: inline-block;
border: none;
margin: 0;
height: 50px;
}
.radio-blue .radio-icon:after {
background: #2196F3 !important;
}
.radio-blue .radio-icon:before {
border-color: #2196F3 !important;
}
.radio-teal .radio-icon:after {
background: #009688 !important;
}
.radio-teal .radio-icon:before {
border-color: #009688 !important;
}
.radio-gray .radio-icon:after {
background: #B6B6B6 !important;
}
.radio-gray .radio-icon:before {
border-color: #B6B6B6 !important;
}
Y ahora utilizaremos la etiqueta de la siguiente manera:
<div ng-app="ionicApp">
<ion-tabs class="tabs-icon-only tabs-positive">
<ion-tab title="Home" icon="ion-ios7-filing">
<ion-header-bar class="bar-positive">
<h1 class="title">Ionic Checkbox</h1>
</ion-header-bar>
<ion-content has-tabs="true" ng-controller="HomeCtrl">
<div>
<ion-radio class="radio radio-inline radio-gray" ng-model="choice" ng-value="'A'">iOS</ion-radio>
<ion-radio class="radio radio-inline radio-teal" ng-model="choice" ng-value="'B'">Android</ion-radio>
<ion-radio class="radio radio-inline radio-blue" ng-model="choice" ng-value="'C'">Windows Phone</ion-radio>
</div>
</ion-content>
</ion-tab>
</ion-tabs>
</div>
¿Verdad que es sencillo? Pues con esto ya lo tendríamos listo.
Nota: en caso de que queramos esconder el icono del componente (o hide ion-radio), o ponerlo transparente (transparent), lo tendríamos que hacer con CSS. Esto no se podría hacer de otra manera por que no hay una variable que nos permita hacerlo. En versiones superiores a Ionic 3 tendríamos que utilizar el siguiente código:
ion-radio{
--color:transparent;
--color-checked: transparent;
}
Y en caso contrario, en Ionic 3 y Ionic 2 sería el siguiente:
.radio-md .radio-icon{
display: none;
}
.item-md{
border: 1px solid color($colors,divider);
border-radius: 6px;
}
.item-radio-checked{
border: 1px solid color($colors,primary);
}
Continuamos hablando de estilos y ahora lo que vamos a ver es cómo podemos asignar una posición al check del button. Para ello tenemos que utilizar una etiqueta que todos hemos visto. Se trata, por ejemplo, de "item-left" y la tendremos que setear cómo vemos en el siguiente ejemplo:
En primer lugar, tenemos esto:
<ion-item>
<ion-label>Etiqueta</ion-label>
<ion-radio checked="true" value="1"></ion-radio>
</ion-item>
Y queremos hacer que el ckeck se vea en el lado izquierdo en lugar del derecho. Para eso vamos a hacer lo siguiente:
<ion-radio checked="true" value="1" item-left></ion-radio>
En caso de que queramos ponerlo al otro lado, solo tendríamos que poner item-right en lugar de item-left y listo :)
Mira, el logo de tu app es una de las partes más importantes de su ficha de Google Play y del ASO que le hagas a tu app. Mejor logo equivale a más descargas y más dinero... Porque entendemos esto, hemos creado esta herramienta para ti. Una herramienta para optimizar, mejorar y evaluar tus logos y espiar los de tu competencia. No te espoileo más, dentro está la explicación.
Y aquí nos despedimos. Espero haberte ayudado y nos vemos en el siguiente artículo. Hasta entonces ¡que vaya bien!