Formularios
Uploader
Elige documentos y conserva el estado de subida en la aplicación.
Importación
import { NeuUploaderComponent } from '@neural-ui/native/uploader';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.
Todo lo que necesita tu proyecto
Elige un archivo pequeño, prueba la subida local, reintenta un fallo provocado y elimina archivos. Conecta el transporte real documentado más abajo en tu aplicación.
Ver y copiar el ejemplo completo
import { Component, signal } from '@angular/core';
import { NeuSwitchComponent } from '@neural-ui/native/switch';
import { NeuUploaderComponent } from '@neural-ui/native/uploader';
import { View, Text, ScrollView } from '@ng-native/components';
import type { NeuUpload, NeuUploadItem } from '@neural-ui/native';
@Component({
selector: 'app-example',
imports: [NeuSwitchComponent, NeuUploaderComponent, 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">Todo lo que necesita tu proyecto</text><text class="product-caption">Reúne el briefing y los documentos de apoyo</text></view>
<view class="product-surface"><neu-uploader [stateLabels]="{ selected: 'Archivo seleccionado', uploading: 'Subiendo', complete: 'Subida completada', error: 'Error de subida' }" label="Documentos del proyecto" chooseLabel="Elegir documentos" uploadLabel="Subir" removeLabel="Eliminar" [(files)]="files" [upload]="upload" [maxFiles]="3" [maxBytes]="5000000" [types]="types" /></view>
<neu-switch label="Hacer fallar la próxima subida simulada" [(checked)]="failNext" />
<text class="product-caption">Simulación local · No se envían archivos a un servidor. Elimina un archivo activo para cancelar; reintenta después de un error.</text></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 files = signal<readonly NeuUploadItem[]>([]);
readonly types = ['application/pdf', 'image/*', 'text/plain'];
readonly failNext = signal(false);
readonly upload: NeuUpload = async (_file, progress, signal) => {
const fail = this.failNext(); this.failNext.set(false);
for (const percent of [25, 60, 100]) {
await this.pause(signal);
if (fail && percent === 60) throw new Error('Error de conexión simulado. Inténtalo de nuevo.');
progress(percent);
}
};
private pause(signal: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
if (signal.aborted) { reject(new Error('Cancelled')); return; }
const abort = () => { clearTimeout(timer); signal.removeEventListener('abort', abort); reject(new Error('Cancelled')); };
const timer = setTimeout(() => { signal.removeEventListener('abort', abort); resolve(); }, 350);
signal.addEventListener('abort', abort, { once: true });
});
}
}Ajusta la configuración y prueba el resultado.
Capturas del dispositivo en inglés.


Conecta una subida real
Elegir un archivo no lo sube. Aporta upload para enviarlo a tu backend o almacenamiento. Este ejemplo nativo utiliza una URL PUT temporal que proporciona tu backend autenticado y Expo FileSystem para leer el archivo elegido.
npx expo install expo-file-systemCrea estos dos archivos, importa ProjectFilesComponent en su padre y colócalo dentro del neu-theme raíz. Sustituye api.example.com por tu endpoint y añade la autenticación de tu aplicación. El backend debe validar permisos, tipo MIME y tamaño y devolver una URL HTTPS específica para ese archivo; los límites del cliente solo orientan al usuario.
import { File } from 'expo-file-system';
import { fetch as expoFetch } from 'expo/fetch';
import type { NeuFile, NeuUpload } from '@neural-ui/native';
type UploadTarget = { url: string; headers?: Record<string, string> };
// Ask your authenticated backend for a short-lived, file-specific PUT URL.
type ResolveTarget = (file: NeuFile, signal: AbortSignal) => Promise<UploadTarget>;
export function createUpload(resolveTarget: ResolveTarget): NeuUpload {
return async (file, progress, signal) => {
if (signal.aborted) throw new Error('Upload cancelled');
progress(0);
const target = await resolveTarget(file, signal);
if (signal.aborted) throw new Error('Upload cancelled');
const response = await expoFetch(target.url, {
method: 'PUT', signal,
headers: { 'Content-Type': file.mimeType || 'application/octet-stream', ...target.headers },
body: new File(file.uri),
});
if (!response.ok) throw new Error('Could not upload. Try again.');
if (signal.aborted) throw new Error('Upload cancelled');
progress(100);
};
}import { Component, signal } from '@angular/core';
import { NeuUploaderComponent } from '@neural-ui/native/uploader';
import type { NeuUploadItem } from '@neural-ui/native';
import { createUpload } from './upload-transport';
@Component({
selector: 'app-project-files',
imports: [NeuUploaderComponent],
template: `<neu-uploader label="Project documents" chooseLabel="Choose documents"
uploadLabel="Upload" removeLabel="Remove" [(files)]="files"
[upload]="upload" [types]="types" [maxFiles]="3" [maxBytes]="5000000" />`,
})
export class ProjectFilesComponent {
readonly files = signal<readonly NeuUploadItem[]>([]);
readonly types = ['application/pdf', 'image/*'];
readonly upload = createUpload(async (file, signal) => {
// Replace this endpoint and add your application's authentication.
const response = await fetch('https://api.example.com/uploads/target', {
method: 'POST', signal,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: file.name, size: file.size, type: file.mimeType }),
});
if (!response.ok) throw new Error('Could not prepare the upload. Try again.');
const target: unknown = await response.json();
if (!target || typeof target !== 'object' || !('url' in target) || typeof target.url !== 'string') {
throw new Error('Invalid upload destination');
}
const url = new URL(target.url);
if (url.protocol !== 'https:') throw new Error('An HTTPS upload destination is required');
return { url: url.href };
});
}El callback recibe el archivo, una función de progreso y un AbortSignal. Resuelve solo tras una respuesta correcta del servidor; lanza un error si falla para que Uploader muestre el mensaje y permita reintentar. Eliminar un archivo activo o destruir Uploader cancela la señal. Pásala a todas las peticiones de red.
Este transporte fetch comunica 0 y 100; no mide los bytes enviados durante la petición. Utiliza un transporte con eventos de progreso si necesitas porcentajes intermedios. Eliminar un elemento ya subido solo lo quita de la interfaz: borrar el objeto remoto requiere la API de tu aplicación. La demo web simula la subida localmente y no envía archivos a un servicio externo.
Referencia API
Inputs
| Nombre | Tipo | Predeterminado | Enlace | Descripción |
|---|---|---|---|---|
| label | string | 'Files' | Input | Nombre legible del control, acción o grupo. |
| disabled | boolean | false | Input | Desactiva la interacción del usuario. La aplicación puede seguir proporcionando valores. |
| types | readonly string[] | ['*/*'] | Input | Tipos MIME de archivos admitidos que se pasan al selector y a las comprobaciones de aceptación. |
| multiple | boolean | true | Input | Permite varias opciones o archivos cuando se admite; consulta el tipo declarado del model. |
| maxFiles | number | 10 | Input | Cantidad máxima de archivos admitidos por el uploader. |
| maxBytes | number | 10 * 1024 * 1024 | Input | Tamaño máximo admitido por archivo, en bytes. |
| chooseLabel | string | 'Choose files' | Input | Texto o nombre accesible utilizado para elegir archivos. No ejecuta la acción por sí solo. |
| uploadLabel | string | 'Upload' | Input | Texto o nombre accesible utilizado para subir archivos. No ejecuta la acción por sí solo. |
| removeLabel | string | 'Remove' | Input | Texto o nombre accesible utilizado para eliminar. No ejecuta la acción por sí solo. |
| stateLabels | Readonly<Record<"selected" | "uploading" | "complete" | "error", string>> | { selected: 'File selected', uploading: 'Uploading', complete: 'Upload complete', error: 'Upload failed', } | Input | Mensajes visibles para selected, uploading, complete y error. Aporta las cuatro claves en el idioma de tu aplicación; los IDs de estado en files no cambian. |
| upload | NeuUpload | undefined | undefined | Input | Callback de subida de tu aplicación. Recibe el archivo, una función de progreso y un AbortSignal; seleccionar archivos no exige un transporte. |
Models
| Nombre | Tipo | Predeterminado | Enlace | Descripción |
|---|---|---|---|---|
| files | readonly NeuUploadItem[] | [] | Model · bidireccional | Archivos actuales, incluido su estado de subida y progreso. Enlaza una signal escribible mediante [(files)]. |
Outputs
| Nombre | Payload | Cuándo se emite |
|---|---|---|
| filesChange | readonly NeuUploadItem[] | Emite el valor actualizado de files. [(files)] enlaza este output junto con su input model. |
| selected | readonly NeuFile[] | Emite los archivos aceptados por el selector; es independiente de la finalización de la subida. |
| completed | NeuFile | Emite el archivo cuyo callback de subida terminó correctamente. |
| rejected | string | Emite el mensaje de rechazo de un archivo que no superó las comprobaciones de aceptación. |
Estado de los models
Enlaza la signal con [(nombreModel)] sin llamarla. Utiliza nombreModel() para leer su valor.
import { signal } from '@angular/core';
import type { NeuUploadItem } from '@neural-ui/native';
// Fields inside your component class
readonly files = signal<readonly NeuUploadItem[]>([]);Tipos e interfaces de datos
Utiliza los imports en tu aplicación. Las definiciones siguientes se muestran como referencia.
NeuFile
import type { NeuFile } from '@neural-ui/native';
// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuFile {
readonly uri: string;
readonly name: string;
readonly size?: number;
readonly mimeType?: string;
}| Campo | Tipo | Requisito | Significado |
|---|---|---|---|
| uri | string | Obligatorio | URI nativa de imagen o archivo. |
| name | string | Obligatorio | Nombre de archivo proporcionado por el selector. |
| size | number | Opcional | Tamaño en bytes, cuando lo proporciona el selector. |
| mimeType | string | Opcional | Tipo MIME del archivo, si está disponible. |
NeuUploadItem
import type { NeuUploadItem } from '@neural-ui/native';
import type { NeuFile } from '@neural-ui/native';
// Public definition for reference only. Import this type; do not redeclare it.
export interface NeuUploadItem {
readonly id: string;
readonly file: NeuFile;
readonly state: 'selected' | 'uploading' | 'complete' | 'error';
readonly progress: number;
readonly error?: string;
}| Campo | Tipo | Requisito | Significado |
|---|---|---|---|
| id | string | Obligatorio | Identificador estable y único para seguimiento y selección. |
| file | NeuFile | Obligatorio | Datos del archivo seleccionado con el contrato NeuFile. |
| state | 'selected' | 'uploading' | 'complete' | 'error' | Obligatorio | Fase de subida: selected, uploading, complete o error. |
| progress | number | Obligatorio | Progreso de la subida como porcentaje. |
| error | string | Opcional | Mensaje de error de subida opcional. |
NeuUpload
import type { NeuUpload } from '@neural-ui/native';
import type { NeuFile } from '@neural-ui/native';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuUpload = (file: NeuFile, progress: (percent: number) => void, signal: AbortSignal) => Promise<void>;