Angular 9+: Input con copiado al portapapeles
Angular 9: Novedades
Hace un tiempo que llegó la tan esperada versión 9 de Angular, con cambios internos profundos que prometen mejorar aspectos cruciales como la velocidad de la aplicación, tiempo de compilación, y reducción del bundle de producción entre otros. Lo más destacable de esta nueva versión es el nuevo motor de renderizado Ivy, aunque disponible opcionalmente desde la versión 8, a partir de la novena viene habilitado por defecto.
Estos cambios no van a provocar que cambie nuestra manera de programar con Angular, gracias al enorme esfuerzo que han puesto los desarrolladores en mantener la retrocomptibilidad con las versiones anteriores. Una prueba de ello son los 14 release candidates publicados antes del primer release estable oficial, el cual se retrasó más de un mes de su planificación (planificado para Diciembre 2019, liberado la primera semana de Febrero 2020).
Ya existen numerosos posts hablando sobre las novedades (las que no cambiarán nuestra forma de programar), por ejemplo éste o éste. Y en caso de necesitar ayuda para actualizar tu versión puedes consultar la guía oficial de actualización. Es por ello que vamos a ver una novedad que se ha introducido en Angular Material 9, y que aún siendo más discreta, nos puede ser de gran utilidad.
Estamos hablando del nuevo paquete Clipboard. Gracias a él tenemos una herramienta para interactuar con el portapapeles del sistema, y copiar textos de cualquier longitud de una forma muy sencilla.
Objetivo
Vamos a desarrollar una caja de texto que integre un botón lateral para copiar el texto que escribamos. Al pulsar el botón, mostraremos un snackbar (también de Angular Material, por supuesto) en la parte inferior de la pantalla informando de ello. ¡Veréis que chulo queda! 🤩
Tecnologías
En el desarrollo del post utilizaremos el siguiente stack de versiones.
- Angular CLI 9.0.4
- Angular Material 9.1.0
- Angular CDK 9.1.0
- Node 12.14.1
- Npm 6.13.7
- Typescript 3.7.5
Creación del proyecto
Vamos a crear el proyecto sin el Router de Angular y preferiblemente con el preprocesador de estilos SCSS. Después nos movemos a la carpeta del proyecto y ahí instalamos Angular Material (incluye el CDK y Angular Animations). Podemos dejar las opciones del prompt por defecto.
ng new angular-input-clipboard
cd angular-input-clipboard
ng add @angular/material
A continuación importamos los módulos que vamos a necesitar.
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { AppComponent } from './app.component';
import { ClipboardModule } from '@angular/cdk/clipboard';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ReactiveFormsModule } from '@angular/forms';
import { MatSnackBarModule } from '@angular/material/snack-bar';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
ReactiveFormsModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatButtonModule,
ClipboardModule,
MatSnackBarModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Input
Eliminamos todo el contenido del archivo app.component.html, ya que aquí crearemos nuestro input. Para ello declaramos un mat-form-field, y dentro de éste un mat-label, un input nativo, y un botón con un mat-icon dentro.
<mat-form-field appearance="standard" color="primary">
<mat-label>Escribe un texto</mat-label>
<input matInput type="text" />
<button mat-icon-button matSuffix color="primary">
<mat-icon>file_copy</mat-icon>
</button>
</mat-form-field>
Clipboard
Vemos que va tomando forma poco a poco. Nos quedaría la parte de funcionalidad, en la que se copia el texto del input cuando se hace click en el botón.
Lo primero es declarar un FormControl, importar los servicios correspondientes al clipboard y al snackbar en el constructor, e inicializar el FormControl.
Después creamos la función que se ejecutará cuando se haga click en el botón. En ella se copia el texto al portapapeles y se abre el snackbar.
app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Clipboard } from '@angular/cdk/clipboard';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({ ... })
export class AppComponent {
// Input donde escribiremos el texto a copiar
textControl: FormControl;
constructor(
private clipboard: Clipboard, // Servicio para usar el portapapeles
private snackBar: MatSnackBar // Servicio para usar snackbars
) {
// Se inicializa el input como FormControl
this.textControl = new FormControl();
}
/**
* Copia el texto del input en el portapapeles y muestra un snackbar
*/
copyToClipboard(): void {
// Se copia el texto del input al portapapeles
this.clipboard.copy(this.textControl.value);
// Se muestra un snackbar durante 2 segundos en la parte inferior
this.snackBar.open('¡Texto copiado al portapapeles!', null, {
duration: 2000,
panelClass: 'snackbar'
});
}
}
Actualizamos el archivo app.component.html para incluir estos cambios. Y de paso aplicamos un par de reglas de estilo.
app.component.html
<div class="container">
<mat-form-field appearance="standard" color="primary" class="field">
<mat-label>Escribe un texto</mat-label>
<input matInput type="text" [formControl]="textControl" />
<button mat-icon-button matSuffix color="primary"
[disabled]="!textControl.value?.trim()"
(click)="copyToClipboard()">
<mat-icon>file_copy</mat-icon>
</button>
</mat-form-field>
</div>
app.component.scss
.container {
display: flex;
.field {
margin: auto;
}
}
styles.scss
html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
.snackbar {
display: flex !important;
place-content: center;
}
Resultado
Hemos añadido al botón de copiado una regla de deshabilitado. Así sólo estará activo cuando se escriba algo, y no sean solo espacios en blanco. Aunque en este post la implementación del clipboard ha sido en un botón lateral de un input, está de sobra decir que se puede implementar cómo y donde uno quiera. A continuación podéis ver el resultado.
Recursos
Además del ejemplo que hemos desarrollado, en la documentación del clipboard de Angular se detalla un caso especial. Cuando se quieren copiar textos largos puede darse el caso que no se copie entero, y se expone un método alternativo. En el stackblitz que os dejo a continuación tenéis ambos casos implementados, para textos cortos y largos. ¡Espero que os sirva!
Analyst Frontend Developer en Comunytek (BBVA CIB – NOVA).
Autodidacta, apasionado de las nuevas tecnologías y de los proyectos DIY.