Gráficos interactivos con Angular y SVG

https://ruthvargas.com/wp-content/uploads/2021/03/angular-svg.jpg

Una característica muy interesante en Angular es que a partir de su versión 8 tiene la capacidad de usar archivos SVG como plantilla en sus aplicaciones, pero ¿para qué nos sirve esto?. Cuando se usa un SVG como plantilla, puedes utilizar directivas y enlaces como con las plantillas HTML. Con estas características, puede generar gráficos interactivos de forma dinámica.

Veamos que se quiere

Compra de butacas

En una aplicación de compra de tickets para el cine es necesario visualizar las butacas que se encuentran disponibles y no disponibles, además de poder seleccionar la que deseamos comprar, esta funcionalidad la podemos realizar en Angular, construyendo un mapa interactivo utilizando SVG.

El resultado que buscamos es algo parecido a esto:

Sala de cine

P1. Crear una imagen vectorial

Hay muchas herramientas para trabajar con imágenes vectoriales, en este caso, utilice Illustrator para crear una imagen vectorial de la sala de cine con todas sus butacas, cada una dibujada de manera individual.

P2. Exportar a SVG

La importancia de que cada dibujo este independiente es para obtener un path para cada segmento de nuestra sala, y se pueda colorear cada pieza según sea necesario.

SVG exportado desde Illustrator

P3. Crear proyecto en Angular

Lo primero que necesitamos es crear un proyecto en Angular, podemos utilizar de manera local comandos de Angular Cli para agilizar el proceso, o trabajar en línea con stackblitz

   
<!-- Crear proyecto -->  
> ng new angular-svg-cinema

<!-- Archivos-->  
|-- app
     |-- app.component.css
     |-- app.component.html
     |-- app.component.ts
     |-- app.module.ts
     |-- data.ts
     |-- enum.ts

P4. Preparar la data

Con los códigos del archivo SVG generado, se creó un objeto adicionando lo necesario para manejar las propiedades de cada segmento como color, estado, nombre o código de la butaca.

Esta data podría estar de manera externa en una tabla de base de datos.

    
/* data.ts */    
export let DATA = [
  {
    code: "A1",
    path:
      "M133.51,102.97c-1.56,0-2.83,1.28-2.83,2.83v3.59c-1.24-3.16-4.32-5.42-7.91-5.42h-8.5 c-3.59,0-6.67,2.26-7.91,5.42v-3.59c0-1.56-1.28-2.83-2.83-2.83s-2.83,1.28-2.83,2.83v17.01c0,1.57,1.27,2.83,2.83,2.83 c0.92,0,1.72-0.45,2.24-1.13v4.96c0,1.56,1.28,2.83,2.83,2.83h19.84c1.56,0,2.83-1.28,2.83-2.83v-4.96 c0.52,0.68,1.33,1.12,2.24,1.12c1.56,0,2.83-1.28,2.83-2.83V105.8C136.35,104.25,135.07,102.97,133.51,102.97z",
    color: "#CAC8C9",
    status: "Disponible"
  },
  {
    code: "A2",
    path:
      "M169.88,102.97c-1.56,0-2.83,1.28-2.83,2.83v3.59c-1.24-3.16-4.32-5.42-7.91-5.42h-8.5 c-3.59,0-6.67,2.26-7.91,5.42v-3.59c0-1.56-1.28-2.83-2.83-2.83s-2.83,1.28-2.83,2.83v17.01c0,1.57,1.27,2.83,2.83,2.83 c0.92,0,1.72-0.45,2.24-1.13v4.96c0,1.56,1.28,2.83,2.83,2.83h19.84c1.56,0,2.83-1.28,2.83-2.83v-4.96 c0.52,0.68,1.33,1.12,2.24,1.12c1.56,0,2.83-1.28,2.83-2.83V105.8C172.71,104.25,171.44,102.97,169.88,102.97z",
    color: "#CAC8C9",
    status: "Disponible"
  },
  /* ... */
];


Ver completo

P5. Vincular con el componente

En el componente principal, sin entrar en lógica de negocio, lo que tenemos que hacer es vincular cada segmento para que obtenga el color y estado apropiado, utilizando los atributos de SVG como fill y stroke.

De igual manera podemos utilizar las directivas de angular como ngFor, ngStyle o ngClass.

     
    <!-- app.component.html -->
    <div class="content">
      <h1> Selección de butacas </h1>
      <hr>

      <svg id="mapsvg" class="map" stroke="transparent" fill-opacity="1">
        <svg:path *ngFor="let item of data; let i = index"
          [attr.data-index]="i"
          id="item.code{{i}}"
          [attr.d]="item.path"
          [ngStyle]="{ fill : item.color }"
          [ngClass]="'menu-cursor-pointer'"
          (click)="reserve(i)">
          <title>{{item.code}}</title>
        </svg:path>
      </svg>
    </div>
  
  

En app.component.ts, cargamos la DATA al iniciar el componente, además agregamos el método reserve(), que se ejecutará cuando el usuario haga click sobre una de las butacas.

Este método cambia el color del segmento y asigna es estado Seleccionado.

    
/* app.component.ts */    
import { Component } from "@angular/core";
import { DATA } from "./data";
import { EnumColor, EnumStatus } from "./enum";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  data = [];

  ngOnInit() {
    this.data = DATA;
  }

  reserve(i: number) {
    if (this.data[i].code && this.data[i].color == EnumColor.DISPONIBLE) {
      this.data[i].color = EnumColor.SELECCIONADO;
      this.data[i].status = EnumStatus.SELECCIONADO;
    }
  }
}

También se creó un enumerado para los estados y colores.

    
/* enum.ts */    
export enum EnumColor {
  DISPONIBLE = "#CAC8C9",
  OCUPADO = "#FFD348",
  SELECCIONADO = "#0DA037"
}

export enum EnumStatus {
  DISPONIBLE = "Dsiponible",
  OCUPADO = "Ocupado",
  SELECCIONADO = "Tu selección"
}

Se realizaron algunos ajustes de estilo

        
/* app.component.css */
.content {
  max-width: 700px;
  margin: auto;
}

.map {
  min-height: 100vh;
  width: 100%;
}

svg {
  overflow: hidden;
  vertical-align: middle;
}


¡Esto es todo! ¿Tenías idea de las cosas geniales que puedes construir con SVG y Angular? ¡Te leo en los comentarios!

Descargar en GitHub o stackblitz.

3 respuestas a “Gráficos interactivos con Angular y SVG”

  1. Luis Enrique Illescas dice:

    Excelente contenido, auxilia para proyectos de la universidad. Gracias 😀

Deja una respuesta

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