Saltar al contenido principal
Neural Nativev0.1.0-beta.1Documentación

Importación

component.ts
import { NeuSelectComponent } from '@neural-ui/native/select';

Ejemplos

Importa ExampleComponent en su padre y coloca <app-example /> dentro del neu-theme raíz de tu aplicación. El preview aporta el marco y las zonas seguras; utiliza la estructura raíz de Instalación en tu app.

Reúne a las personas adecuadas

Compara selección única y múltiple en una invitación. La búsqueda filtra la lista, los nombres identifican los equipos y la vista previa conserva la invitación local.

Ver y copiar el ejemplo completo
basic.component.ts
import { Component, signal, computed } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuAlertComponent } from '@neural-ui/native/alert';
import { NeuSelectComponent } from '@neural-ui/native/select';
import { View, Text, ScrollView } from '@ng-native/components';
import type { NeuOption } from '@neural-ui/native';

@Component({
  selector: 'app-example',
  imports: [NeuButtonComponent, NeuAlertComponent, NeuSelectComponent, View, Text, ScrollView],
  template: `
    <scroll-view class="product-scroll" keyboardShouldPersistTaps="handled" [showsVerticalScrollIndicator]="true"><view class="product-stack"><view class="product-section"><text class="product-title" accessibilityRole="header">Reúne a las personas adecuadas</text><text class="product-caption">Un responsable, varios equipos invitados</text></view>
    <view class="product-surface"><neu-select closeLabel="Cerrar opciones" emptyLabel="No hay opciones coincidentes" searchPlaceholder="Escribe para filtrar" searchLabel="Buscar opciones" label="Equipo" [options]="teams" [(value)]="team" [searchable]="true" placeholder="Elige un equipo" />
    <text class="product-caption">Equipo responsable: {{ teamLabel() }}</text></view>
    <view class="product-surface"><neu-select closeLabel="Cerrar opciones" emptyLabel="No hay opciones coincidentes" searchPlaceholder="Escribe para filtrar" searchLabel="Buscar opciones" label="Invitar equipos" [options]="teams" [multiple]="true" [searchable]="true" [(values)]="invitedTeams" placeholder="Elige equipos" />
    <text class="product-caption">{{ invitedLabels() || 'Todavía no hay equipos invitados' }}</text>
    <view class="product-actions"><neu-button label="Ver invitación" size="sm" variant="outline" [disabled]="!invitedTeams().length" (pressed)="invitation.set(invitedLabels())" /></view></view>
    @if (invitation()) { <neu-alert closeLabel="Cerrar aviso" title="Vista previa de la invitación" [message]="invitation()" tone="info" /> }</view></scroll-view>
  `,
  styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }

:host { flex: 1; flex-shrink: 1; min-height: 0; }
.product-scroll { flex: 1; flex-shrink: 1; height: 0px; min-height: 0; }
.product-stack { gap: 24px; padding-bottom: 20px; }
.product-section { gap: 12px; }
.product-row { flex-direction: row; align-items: center; gap: 12px; }
.product-copy { flex: 1; min-width: 0; gap: 4px; }
.product-title { color: var(--neu-text, #172033); font-size: 24px; line-height: 30px; font-weight: 600; }
.product-heading { color: var(--neu-text, #172033); font-size: 17px; line-height: 23px; font-weight: 600; }
.product-caption { color: var(--neu-muted, #596477); font-size: 14px; line-height: 20px; }
.product-actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.product-surface { gap: 16px; padding: 16px; border-radius: 16px; background-color: var(--neu-surface, #ffffff); border-width: 1px; border-color: var(--neu-border, #a5afbf); }

  `,
})
export class ExampleComponent {
  readonly teams: readonly NeuOption[] = [{ id: 'design', label: 'Diseño', description: 'Interfaces de producto' }, { id: 'engineering', label: 'Ingeniería', description: 'Desarrollo de aplicaciones' }, { id: 'archive', label: 'Equipo archivado', disabled: true }];
  readonly team = signal('design');
  readonly invitedTeams = signal<readonly string[]>(['design']);
  readonly teamLabel = computed(() => this.teams.find(team => team.id === this.team())?.label || '');
  readonly invitedLabels = computed(() => this.teams.filter(team => this.invitedTeams().includes(team.id)).map(team => team.label).join(', '));
  readonly invitation = signal('');
}

Plantillas de opciones y selección

Personaliza las opciones y el valor seleccionado. Select conserva la búsqueda, las opciones desactivadas y la selección.

Ver y copiar el ejemplo completo
custom-templates.component.ts
import { Component, signal } from '@angular/core';
import { NeuAvatarComponent } from '@neural-ui/native/avatar';
import { NeuSelectComponent } from '@neural-ui/native/select';
import { View, Text, ScrollView } from '@ng-native/components';
import { NeuSelectItemDirective, NeuSelectSelectedDirective } from '@neural-ui/native/select';

@Component({
  selector: 'app-example',
  imports: [NeuAvatarComponent, NeuSelectComponent, NeuSelectItemDirective, NeuSelectSelectedDirective, View, Text, ScrollView],
  template: `
    <scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
    <neu-select closeLabel="Cerrar opciones" emptyLabel="No hay opciones coincidentes" searchPlaceholder="Escribe para filtrar" searchLabel="Buscar opciones" label="Equipo del proyecto" [options]="teams" [(value)]="team" [searchable]="true" placeholder="Elige un equipo">
      <ng-template neuSelectItem let-option let-selected="selected" let-disabled="disabled">
        <view class="team-option"><neu-avatar [label]="option.label" size="sm" /><view class="team-copy"><text [class]="selected ? 'team-selected' : 'usage-heading'">{{ option.label }}</text><text [class]="selected ? 'team-selected-caption' : 'usage-caption'">{{ disabled ? 'No disponible' : option.description }}</text></view></view>
      </ng-template>
      <ng-template neuSelectSelected let-options let-placeholder="placeholder">
        @if (options.length) { <view class="selected-team"><neu-avatar [label]="options[0].label" size="sm" /><text>{{ options[0].label }}</text></view> }
        @else { <text>{{ placeholder }}</text> }
      </ng-template>
    </neu-select>
    <text class="usage-caption">ID del equipo seleccionado: {{ team() || 'Ninguno' }}</text>
    </view></scroll-view>
  `,
  styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }

.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }

.team-option, .selected-team { flex-direction: row; gap: 10px; align-items: center; } .team-copy { flex: 1; min-width: 0; gap: 3px; } .team-selected { color: var(--neu-accent, #006fe6); font-weight: 600; } .team-selected-caption { color: var(--neu-muted, #596477); font-size: 14px; }
  `,
})
export class ExampleComponent {
  readonly teams = [
    { id: 'design', label: 'Diseño', description: 'Interfaces de producto' },
    { id: 'engineering', label: 'Ingeniería', description: 'Aplicaciones móviles' },
    { id: 'archive', label: 'Equipo archivado', description: 'No admite nuevos miembros', disabled: true },
  ];
  readonly team = signal('design');
}
Playground interactivo

Ajusta la configuración y prueba el resultado.

Capturas del dispositivo en inglés.

Select · android · native capture
AndroidCaptura nativa
Select · ios · native capture
iOSCaptura nativa

Personalización

Importa estas directivas junto con el componente y añádelas a Component.imports. Los ejemplos anteriores incluyen la configuración completa.

personalization-imports.ts
import { NeuSelectItemDirective, NeuSelectSelectedDirective } from '@neural-ui/native/select';
Plantilla o regiónContextoUso
ng-template[neuSelectItem]$implicit: NeuOption; index: number; selected: boolean; disabled: booleanContenido visual de la opción. La fila mantiene la selección, el nombre accesible y su estado marcado. index sigue las opciones filtradas.
ng-template[neuSelectSelected]$implicit: readonly NeuOption[]; value: string; values: readonly string[]; placeholder: stringContenido del control, incluido su estado vacío. Las opciones conservan su orden original; en modo múltiple utiliza values y en modo único utiliza value.

Contextos de las plantillas

template-contexts.ts
import type { NeuSelectItemContext, NeuSelectSelectedContext } from '@neural-ui/native/select';

let-item recibe $implicit; los campos con nombre utilizan let-index="index". Usa los tipos exportados al declarar un TemplateRef reutilizable. Mantén el contenido interno como presentación: el padre conserva la acción accesible y el comportamiento de selección.

Referencia API

Inputs

NombreTipoPredeterminadoEnlaceDescripción
optionsreadonly NeuOption[][]InputOpciones disponibles. Los valores seleccionados se refieren a sus IDs, no a las etiquetas visibles.
placeholderstring'Choose'InputEjemplo o indicación mientras el valor está vacío; conserva una etiqueta independiente.
closeLabelstring'Dismiss options'InputTexto o nombre accesible utilizado para cerrar. No ejecuta la acción por sí solo.
multiplebooleanfalseInputUtiliza casillas y mantiene abierto el desplegable para elegir varias opciones. Enlaza values en este modo; value se reserva para selección simple.
searchablebooleanfalseInputMuestra un buscador dentro del desplegable. Filtra etiquetas y descripciones sin distinguir acentos ni mayúsculas. La búsqueda se reinicia al abrirlo.
searchLabelstring'Search options'InputNombre accesible del buscador del desplegable. No se muestra como encabezado independiente; searchPlaceholder proporciona el texto visible dentro del campo.
searchPlaceholderstring'Type to filter'InputPlaceholder visible cuando la búsqueda del desplegable está vacía.
emptyLabelstring'No matching options'InputTexto o nombre accesible utilizado para estado vacío. No ejecuta la acción por sí solo.
labelstringObligatorioInputNombre legible del control, acción o grupo.
disabledbooleanfalseInputDesactiva la interacción del usuario. La aplicación puede seguir proporcionando valores.
readonlybooleanfalseInputImpide editar o seleccionar desde el control y conserva el valor actual.
invalidbooleanfalseInputMarca el control como inválido. El formulario o la aplicación aporta la regla de validación.
touchedbooleanfalseInputEstado de interacción con el campo, aportado manualmente o mediante Signal Forms.
hintstring''InputAyuda del campo que aparece cuando no se muestra un mensaje de error.
requiredbooleanfalseInputPresentación de campo obligatorio. Define la regla de validación real en el formulario de tu aplicación.
requiredLabelstring'Required'InputRequisito traducido que se anuncia con la ayuda cuando required es true. Required por defecto; no valida el valor.
errorstring''InputMensaje de error aportado por la aplicación; combínalo con el estado invalid del control.

Models

NombreTipoPredeterminadoEnlaceDescripción
valuesreadonly string[][]Model · bidireccionalIDs seleccionados en modo múltiple. Filtrar conserva las selecciones que quedan fuera de los resultados. El modo simple utiliza value.
valuestring''Model · bidireccionalID de la opción seleccionada en modo simple. El modo múltiple utiliza el array independiente values.

Outputs

NombrePayloadCuándo se emite
valuesChangereadonly string[]Emite el valor actualizado de values. [(values)] enlaza este output junto con su input model.
valueChangestringEmite el valor actualizado de value. [(value)] enlaza este output junto con su input model.
touchvoidComunica la interacción del usuario al formulario o aplicación que lo contiene.

Estado de los models

Enlaza la signal con [(nombreModel)] sin llamarla. Utiliza nombreModel() para leer su valor.

model-fields.ts
import { signal } from '@angular/core';

// Fields inside your component class
readonly values = signal<readonly string[]>([]);
readonly value = signal<string>('');

Tipos e interfaces de datos

Utiliza los imports en tu aplicación. Las definiciones siguientes se muestran como referencia.

NeuOption

NeuOption.ts
import type { NeuOption } from '@neural-ui/native';

// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuOption {
    readonly id: string;
    readonly label: string;
    readonly description?: string;
    readonly disabled?: boolean;
}
CampoTipoRequisitoSignificado
idstringObligatorioIdentificador estable y único para seguimiento y selección.
labelstringObligatorioNombre legible del elemento.
descriptionstringOpcionalTexto de apoyo opcional; su presentación depende del control.
disabledbooleanOpcionalImpide activar o seleccionar este elemento.