Saltar al contenido

Upload File Input Component con Angular Material

Desde hace algún tiempo que vengo trabajando con Angular, recientemente en un freelo me vi en la necesidad de hacer la carga de un archivo a un controllador en net core. Actualmente en este proyecto se esta usando Angular Material y me di con la sorpresa que en esta librería no se tienen ninguna alternativa a un input type file.

Luego de buscar un poco y no encontrar una alternativa que me llamara la atención, decidí ver la forma de crear una. Les comparto esta demo por si a alguien le puede resultar útil.

Primero, necesitamos crear nuestro nuevo proyecto en angular, instalar la librería de angular material, crear el componente para cargar el archivo y finalmente, crear un modulo adicional para que concentre todos los módulos correspondientes de angular material, para esto ejecutamos en la consola del vscode los típicos comandos:

ng new UploadFileAngularMaterial
cd UploadFileAngularMaterial
npm install --save @angular/material @angular/cdk @angular/animations
ng g m /modules/material
ng g c file-upload

Del lado de la configuración del angular material, no me explayaré mucho dado que hay muchos tutoriales en la red que pueden dar más ejemplo y también puede verlo en el repositorio al final del post.

Para usar los material icons, estoy en el index de la aplicación poniendo el link a la url:

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

Veamos ahora si lo importante de esta demo:

File-Upload

El html del componente tendrá la siguiente estructura:

HTML del componente File-Upload

Como se puede observar, tenemos un botón para hacerla carga (una imagen), y debajo de este una lista de los archivos que queramos cargar, en la cual se muestra el nombre del archivo, una barra de progreso, y dos iconos que actúan a modo de botones para reintentar en caso la carga no se haya podido efectuar y para cancelar la carga. Finalmente, nuestro input type file.

Sólo explicaré los métodos más importantes del componente dado que se tienen creado varios:

Variables del Componente

Para nuestro componente, tenemos algunas variables que pueden ser input del componente, y un output del tipo string. Además, la información que se selecciones los ‘files’ so del tipo ‘FileUploadModel’, paso a detallar lo que contiene este model.

Model FileUploadModel

Este modelo posee cierta información que usaremos para manejar la carga del archivo, los estamos que tiene, si se puede re-intentar o cancelar así como las subscripciones que se realizan cuando se hacen las peticiones http.

onClick al botón

Cada ves que se realice un click al botón, se está obteniendo el input type file y se busca el evento ‘onchange’, mediante el cual se obtiene los items seleccionados (archivos de texto para el demo). Dado que el evento ‘onchange’, se va activar cuando este cambie, se llama al evento ‘click’ para que abra el buscar de archivos desde el navegador, de manera que cuando estos sean seleccionados se carguen los archivos, y recién se llama al método ‘uploadFiles’.

uploadFiles

Se empieza a iterar sobre los archivos seleccionados que se llenaron en el evento ‘onchange’ y cada uno de estos estos serán enviados al método ‘uploadFile’.

uploadFile

Este es el método que se encarga de hacer la carga del archivo, para la demo estoy usando un api pública «http://file.io» que te permite realizar la carga de archivos. También se podría llamar a algún service de ser necesario o el modo en le cual deseen consumir servicios desde su aplicativo. Acá, estamos inicializando un HttpRequest del tipo post, y asignado el request a nuestro ‘sub’ que es parte de nuestra entidad. Adicionalmente, vamos a mapear un evento para poder determinar el avance del progress conforme se va cargando el archivo, tenemos una suscripción para manejar lo que nos retorne la suscripción en caso de que la respuesta HTPP sea del tipo 200, si fuera alguna respuesta HTTP de error, entrará a nuestro ‘catchError’. De ser correcto, limpiamos el archivo y le enviamos la respuesta al output del tipo eventEmitter, al componente que este llamandonos. Veamos una demo de como se está dando todo este proceso:

Presionamos el botón
Luego de seleccionarlo, se carga al array de archivos y se envia al uploadfiles
Esta por llevarse a cabo la solicitud al endpoint
Mientras el archivo carga, se ve la barra de carga, nombre del archivo y un botón de cancelar
Cuando cargo correctamente, se elimina el archivo del listado y se envía el output
Se envia a un console.log el resultado del envio del archivo

Si quisieramos revisar si la carga fue correcta, podemos abrir el link que se adjunta en el response, teniendo el siguiente resultado.

Respuesta del endpoint

Aún cuando el dedsarrollo del componente me tomo mas tiempo del que hubiera deseado, es una buena idea tenerlo como una herramienta más en el repertorio. Dejo un video de la creación del proyecto y las pruebas que se han realizado, además del link al repositorio.

REPO: GITHUB

Publicado enAngular

13 comentarios

  1. MijailStell MijailStell

    Gracias por el aporte. Ahora entiendo porque no publicabas desde ya casi un mes. Saludos.

    • wgutierrez wgutierrez

      Que tal Mijail, si ando un poco con los tiempos ocupados, pero ya me toca ponerme al día.

  2. raul raul

    Buenas!
    Me estoy mirando tu planteamiento porque tenia el problema este que con material no hay nada para subir fotos, archivos, etc .. pero la petición que haces con el Formdata necesito hacerla en un service mas que nada para separar código entre componente y servicio. Y me topado con el problema de los @Inputs, son necesarios? es que no me aclaro donde esta el padre de estos, me mirado el video y no veo referencia a ellos.
    El @target queda claro pero los demás, nose que función tienen y no se si debo utilizarlos si quiero separar el código entre componente y servicio
    Gracias

    • wgutierrez wgutierrez

      Raul que tal, revise tu consulta. En la demo veo que en el ejemplo esos inputs no son llamados por ningún padre. Sin embargo, en el freelo que estaba haciendo si los llamaba por otro componente y parece que al hacer la demo se me olvidó sacarlos. Por otro lado, en la demo explico puntualmente como implementar esta funcionalidad no entro a detalle de que arquitectura es recomendable seguir en proyectos con angular y la separación de la lógica con la vista., que es lo que tu estas buscando y siempre es recomendable. Espero que te haya podido ayudar en algo.
      Saludos.

  3. raul raul

    Sí, ha sido de ayuda, gracias.
    Porque no sabia que hacer con esos inputs, por cierto, donde se puede ver esa demo?
    Una cosa mas, entonces el @Input param sirve para este ejemplo? porque luego estas haciendo un append con los parámetros (param , file.data) en un Formdata pero no entiendo con que finalidad.
    Gracias! Saludos

  4. Bad_ClarkKent Bad_ClarkKent

    Agradezco el tutorial! estaba buscando algo así (soy practicamente nuevo en la programación 🙂 ).

    Puedo subir consultas al respecto?

    • wgutierrez wgutierrez

      Hola amigo, disculpa la demora. Claro! Saludos.

  5. Steve Steve

    Excelente aporte, viejo! Una unica consulta: como puedo determinar que al abrir la ventana para seleccionar un archivo muestre los de un tipo nada mas? En tu caso, que al abrir el directorio solo te muestre los archivos TXT y no un tipo DOC por ejemplo.
    ¡Saludos!

    • wgutierrez wgutierrez

      Hola Steve, en el repo de github, puede revisar el componente, hay una linea en la que indicas los tipos de extensión que vas a manejar, ahi es donde restringes los tipos de archivo que se pueden seleccionar. Esto, lo puedes poner como una constante en tu aplicación para no estar repitiéndolo, en la demo me parece que lo estoy manejando a nivel del componente. Espero que te ayude. Saludos.

  6. Karen Karen

    Hola como estas, quería preguntarte porque ya no aparece el repositorio en gitbuh con la url que está enlazada? La borras o el link esta mam?

  7. Madelyn Madelyn

    Me puedes ayudar con el link del repo en Github, el link que tienes no funciona.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *