<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es"><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://jolugama.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jolugama.com/" rel="alternate" type="text/html" hreflang="es" /><updated>2024-02-12T23:16:32+00:00</updated><id>https://jolugama.com/feed.xml</id><title type="html">JoLuGaMa Blog</title><subtitle>Blog con manuales y tutoriales de Angular, y otros frameworks javascript</subtitle><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><entry><title type="html">Arduino para programadores en 2023</title><link href="https://jolugama.com/blog/2023/10/13/arduino-para-programadores/" rel="alternate" type="text/html" title="Arduino para programadores en 2023" /><published>2023-10-13T06:12:00+00:00</published><updated>2023-10-13T06:12:00+00:00</updated><id>https://jolugama.com/blog/2023/10/13/arduino-para-programadores</id><content type="html" xml:base="https://jolugama.com/blog/2023/10/13/arduino-para-programadores/"><![CDATA[<h1 id="heading-arduino-para-programadores-en-2023">Arduino para programadores en 2023</h1>

<blockquote>
  <p>Finales del 2023, qué comprar para empezar a usar arduino. Porqué no usar arduino, al menos su controlador, si su entorno y una guía de compra para que programes sin necesidad de entender la electrónica, ya que realmente hay que saber muy pocos conceptos. 
En este post te cuento todo lo que necesitas, con mi experiencia desde 2018. A mis espaldas cuento con una lista larga de errores cometidos,  te escribo para tú no los cometas y consigas que tus proyectos se hagan realidad y no se queden en tu cabeza, o en la mesa cogiendo polvo.</p>
</blockquote>

<h2 id="heading-qué-es-arduino">¿Qué es Arduino?</h2>

<p>Arduino es una plataforma de hardware y software de código abierto que facilita la creación de prototipos electrónicos interactivos. Desde su introducción, ha ganado popularidad gracias a su simplicidad y accesibilidad para principiantes.</p>

<p>Arduino ha revolucionado el mundo de la electrónica y el desarrollo DIY (Do It Yourself) desde su introducción en 2005. Con su enfoque centrado en la facilidad de uso, ha logrado democratizar el acceso a la creación de proyectos electrónicos, permitiendo a principiantes y aficionados de todas las edades y habilidades sumergirse en este fascinante universo.</p>

<h2 id="heading-características-clave-de-arduino">Características clave de Arduino</h2>

<ol>
  <li><strong>Software Amigable</strong>: El IDE (Entorno de Desarrollo Integrado) de Arduino es intuitivo y fácil de usar, haciendo que la programación sea accesible para todos. Su simplicidad permite a los usuarios concentrarse en la lógica del proyecto sin la necesidad de comprender detalles complejos del hardware.</li>
  <li><strong>Hardware Abierto</strong>: Las especificaciones de hardware de Arduino son de código abierto, lo que significa que cualquier persona puede crear y vender su propia versión de una placa Arduino, siempre y cuando respete la licencia.</li>
  <li><strong>Comunidad Activa</strong>: Arduino cuenta con una comunidad global activa que comparte proyectos, bibliotecas y soluciones. Esta red de colaboradores ha sido fundamental para el crecimiento y éxito de la plataforma.</li>
</ol>

<h2 id="heading-la-entrada-en-el-mercado-de-otros-microcontroladores">La entrada en el mercado de otros microcontroladores</h2>

<p>Arduino ha creado un ecosistema que va más allá de sus propias placas y microcontroladores. Su plataforma ha establecido un estándar en la comunidad DIY, y su influencia es evidente en la cantidad de hardware y software que se ha desarrollado inspirado en o para ser compatible con Arduino.</p>

<p>Ha sentado un precedente en el mundo de la electrónica, y su plataforma ha sido adaptada y adoptada por varios otros microcontroladores para aprovechar su entorno y las vastas librerías disponibles. A continuación, te presento una lista de algunos de los microcontroladores más populares que pueden ser programados con el entorno Arduino, ordenados desde los más populares:</p>

<ol>
  <li><strong>ESP8266 y ESP32 (Espressif Systems)</strong>:
    <ul>
      <li><strong>ESP8266</strong> (2014): Un microcontrolador con capacidades WiFi.</li>
      <li><strong>ESP32</strong> (2016): Una evolución del ESP8266 con un procesador más potente, WiFi, y Bluetooth.</li>
    </ul>
  </li>
  <li><strong>STM32 (STMicroelectronics)</strong> (2007):
    <ul>
      <li>Los microcontroladores STM32 son una familia de 32 bits ARM Cortex-M. STM32duino permite usar el IDE de Arduino para programar estos microcontroladores.</li>
    </ul>
  </li>
  <li><strong>Teensy (PJRC)</strong>:
    <ul>
      <li>Teensy ha tenido múltiples versiones desde su lanzamiento. La primera versión fue lanzada alrededor de 2008. Teensy 4.0, por ejemplo, se lanzó en 2019. Estos microcontroladores basados en ARM ofrecen capacidades avanzadas y son compatibles con el Arduino IDE mediante un addon.</li>
    </ul>
  </li>
  <li><strong>ATtiny (Microchip, anteriormente Atmel)</strong>:
    <ul>
      <li>Los ATtiny han estado en el mercado durante mucho tiempo, con variantes como el ATtiny85 siendo populares en proyectos de Arduino de menor escala. Su lanzamiento data de la década de 1990.</li>
    </ul>
  </li>
  <li><strong>SAMD21 y SAMD51 (Microchip)</strong>:
    <ul>
      <li><strong>SAMD21</strong> (alrededor de 2015): Utilizado en placas como Arduino Zero.</li>
      <li><strong>SAMD51</strong>: Una evolución más potente del SAMD21.</li>
    </ul>
  </li>
  <li><strong>nRF52 Series (Nordic Semiconductor)</strong> (2016):
    <ul>
      <li>Estos chips son conocidos por sus capacidades BLE (Bluetooth Low Energy).</li>
    </ul>
  </li>
  <li><strong>Intel Galileo y Edison (Intel)</strong>:
    <ul>
      <li><strong>Intel Galileo</strong> (2013) y <strong>Intel Edison</strong> (2014): Aunque fueron discontinuados, adaptaron el entorno de Arduino.</li>
    </ul>
  </li>
</ol>

<h2 id="heading-esp32-vs-arduino">Esp32 vs Arduino</h2>

<p>Aquí te presento una lista de beneficios al elegir el ESP32 frente a un Arduino tradicional:</p>

<ol>
  <li><strong>Conectividad Integrada</strong>:
    <ul>
      <li><strong>WiFi</strong>: El ESP32 tiene WiFi integrado, lo que permite una fácil conexión a redes y desarrollo de aplicaciones IoT sin hardware adicional.</li>
      <li><strong>Bluetooth y BLE (Bluetooth Low Energy)</strong>: El ESP32 también incorpora capacidades de Bluetooth y BLE, facilitando la conexión con dispositivos móviles y otros dispositivos Bluetooth.</li>
    </ul>
  </li>
  <li><strong>Mayor Potencia de Procesamiento</strong>:
    <ul>
      <li>El ESP32 cuenta con un procesador de doble núcleo basado en el diseño Tensilica Xtensa LX6, con una frecuencia de hasta 240 MHz, lo que le da una capacidad de procesamiento mucho mayor en comparación con las placas Arduino tradicionales.</li>
    </ul>
  </li>
  <li><strong>Mayor Memoria</strong>:
    <ul>
      <li>El ESP32 tiene más RAM y memoria flash que un Arduino, lo que permite manejar aplicaciones más grandes y complejas.</li>
    </ul>
  </li>
  <li><strong>Entrada/Salida Versátil</strong>:
    <ul>
      <li>El ESP32 proporciona una gama más amplia de periféricos, como ADCs de mayor resolución, DACs, touch sensors, entre otros.</li>
    </ul>
  </li>
  <li><strong>Seguridad</strong>:
    <ul>
      <li>Funciones avanzadas como encriptación por hardware, arranque seguro y almacenamiento seguro de claves.</li>
    </ul>
  </li>
  <li><strong>Soporte para Deep Sleep</strong>:
    <ul>
      <li>Permite un consumo muy bajo de energía durante los períodos de inactividad, ideal para aplicaciones alimentadas por batería.</li>
    </ul>
  </li>
  <li><strong>Precio</strong>:
    <ul>
      <li>A pesar de todas sus características avanzadas, el ESP32 suele ser competitivo en precio, a menudo siendo más económico que las placas Arduino con capacidades de conectividad similares.</li>
    </ul>
  </li>
  <li><strong>Compatibilidad con Arduino IDE</strong>:
    <ul>
      <li>Es posible programar el ESP32 utilizando el entorno Arduino, permitiendo a los desarrolladores aprovechar las librerías y herramientas ya familiares.</li>
    </ul>
  </li>
  <li><strong>Desarrollo Activo y Comunidad de Soporte</strong>:
    <ul>
      <li>Dado que el ESP32 ha ganado tanta popularidad, cuenta con una comunidad activa, lo que se traduce en un mayor soporte en foros, tutoriales y librerías actualizadas.</li>
    </ul>
  </li>
  <li><strong>Form Factor Compacto</strong>:</li>
</ol>

<ul>
  <li>Aunque depende de la placa específica, muchos módulos ESP32 son compactos y adecuados para proyectos donde el espacio es una preocupación.</li>
</ul>

<p>Si bien el ESP32 ofrece muchos beneficios, es esencial considerar las necesidades específicas de tu proyecto. En algunos casos, un Arduino tradicional podría ser más que suficiente, y en otros, el ESP32 podría ser la elección clara debido a sus capacidades avanzadas.</p>

<h2 id="heading-esp32-ides-y-lenguajes-de-programación">Esp32, IDEs, y lenguajes de programación</h2>

<p>El ESP32 es una plataforma versátil que admite varios lenguajes de programación. Aquí te detallo algunos de los lenguajes más populares y los IDEs o herramientas asociadas que puedes usar para desarrollar con ellos:</p>

<ol>
  <li><strong>C/C++</strong>:
    <ul>
      <li><strong>Arduino IDE</strong>: Permite programar el ESP32 utilizando el framework de Arduino, que está basado en C/C++.  Solo tienes que cargar una dirección de json en su configuración, y ya puedes usar esp32.</li>
      <li><strong>PlatformIO con VSCode</strong>: Soporta desarrollo en C/C++ para el ESP32 con o sin el framework de Arduino. Es el elegido por la mayoría de los programadores.</li>
      <li><strong>Eclipse</strong>: Puedes configurarlo para el desarrollo en C/C++ para ESP32, especialmente con el plugin Sloeber para soporte de Arduino.</li>
      <li><strong>Espressif IoT Development Framework (ESP-IDF)</strong>: Es el framework oficial de Espressif para el ESP32 y utiliza C/C++.</li>
    </ul>
  </li>
  <li><strong>MicroPython</strong>:
    <ul>
      <li><strong>Thonny</strong>: Un IDE ligero de Python que tiene soporte específico para MicroPython en ESP32.</li>
      <li><strong>uPyCraft</strong>: Un IDE diseñado específicamente para MicroPython y su uso en microcontroladores como ESP32.</li>
      <li><strong>Mu Editor</strong>: Otro IDE que soporta MicroPython y su desarrollo en ESP32.</li>
    </ul>
  </li>
  <li><strong>JavaScript (con Espruino)</strong>:
    <ul>
      <li><strong>Espruino Web IDE</strong>: Es un entorno basado en navegador diseñado específicamente para programar con Espruino, una implementación de JavaScript para microcontroladores.</li>
    </ul>
  </li>
  <li><strong>Lua (con NodeMCU)</strong>:
    <ul>
      <li><strong>ESPlorer</strong>: Un IDE basado en Java para el desarrollo de scripts Lua para ESP32 (y ESP8266) usando el firmware NodeMCU.</li>
    </ul>
  </li>
  <li><strong>Rust</strong>:
    <ul>
      <li>Aunque no es tan común como otros lenguajes, ha habido esfuerzos para llevar Rust al ESP32.</li>
      <li><strong>VSCode o cualquier editor de texto</strong>: Junto con las herramientas de línea de comandos y las bibliotecas adecuadas, puedes desarrollar para ESP32 en Rust.</li>
    </ul>
  </li>
</ol>

<p>Y es aquí cuando te dijo, ¿Qué lenguaje es el que más te gusta? .  Para los amantes de python decirte que no todo se puede hacer con él, no dispone de todas las librerías para su uso, por ejemplo el manejo de la cámara para hacer fotos y videos. Pero para una gran cantidad de proyectos es ideal.</p>

<p>Mi consejo es Usar Platformio (plugin de vscode), con c++ (arduino).</p>

<h2 id="heading-esp32-desde-platformioio-y-vscode">Esp32 desde PlatformioIO y VsCode</h2>

<p>Usar un ESP32 con Arduino en PlatformIO es una excelente opción para aprovechar las capacidades de este microcontrolador con una interfaz amigable y poderosa. Aquí te guiaré paso a paso en la configuración y primeros pasos para trabajar con un ESP32 en PlatformIO:</p>

<ol>
  <li>
    <p><strong>Instalación de Visual Studio Code (VSCode)</strong>:</p>

    <ul>
      <li>Primero, si aún no lo has hecho, descarga e instala <a href="https://code.visualstudio.com/">VSCode</a>.</li>
    </ul>
  </li>
  <li>
    <p><strong>Instalación de PlatformIO en VSCode</strong>:</p>

    <ul>
      <li>Abre VSCode.</li>
      <li>Ve a la sección de extensiones (ícono de bloques en el panel lateral o presiona <code class="language-plaintext highlighter-rouge">Ctrl+Shift+X</code>).</li>
      <li>Busca "PlatformIO" y selecciona "PlatformIO IDE" de la lista, luego haz clic en "Install".</li>
    </ul>
  </li>
  <li>
    <p><strong>Crear un nuevo proyecto</strong>:</p>

    <ul>
      <li>Una vez instalado PlatformIO, en la página de inicio de PlatformIO (la deberías ver al abrir VSCode o al hacer clic en el ícono de PlatformIO en la barra lateral), selecciona "New Project".</li>
      <li>Introduce un nombre para tu proyecto.</li>
      <li>En "Board", escribe "ESP32" y selecciona tu modelo específico de ESP32 (por ejemplo, "Espressif ESP32 Dev Module").</li>
      <li>En "Framework", elige "Arduino".</li>
      <li>Haz clic en "Finish".</li>
    </ul>
  </li>
  <li>
    <p><strong>Estructura del Proyecto</strong>:</p>

    <ul>
      <li>En el explorador de archivos de VSCode, verás varias carpetas y archivos. Los más importantes son:
        <ul>
          <li><code class="language-plaintext highlighter-rouge">src</code>: Aquí es donde colocarás tu código principal (por defecto, tendrás un archivo <code class="language-plaintext highlighter-rouge">main.cpp</code>).</li>
          <li><code class="language-plaintext highlighter-rouge">lib</code>: Es donde puedes colocar bibliotecas adicionales o tus propias bibliotecas.</li>
          <li><code class="language-plaintext highlighter-rouge">platformio.ini</code>: Es el archivo de configuración de tu proyecto. Aquí puedes especificar versiones de frameworks, bibliotecas, flags de compilación y más.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Escribir y Cargar un Programa</strong>:</p>

    <ul>
      <li>Ve al archivo <code class="language-plaintext highlighter-rouge">src/main.cpp</code>.</li>
      <li>Borra cualquier código existente y escribe un simple programa "Blink". Aquí tienes un ejemplo:</li>
    </ul>

    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;Arduino.h&gt;</span><span class="cp">
</span>   
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">pinMode</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>  <span class="c1">// El pin 2 suele ser el LED integrado en la mayoría de los módulos ESP32. otros no tienen, conecta uno junto con una resistencia de 220ohm</span>
<span class="p">}</span>
   
<span class="kt">void</span> <span class="n">loop</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>   <span class="c1">// Enciende el LED.</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>             <span class="c1">// Espera un segundo.</span>
  <span class="n">digitalWrite</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>    <span class="c1">// Apaga el LED.</span>
  <span class="n">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>             
<span class="p">}</span>
</code></pre></div>    </div>

    <ul>
      <li>Conecta tu ESP32 al pc.</li>
      <li>Haz clic en el ícono de "Upload" (una flecha hacia la derecha) en la barra inferior de PlatformIO para compilar y cargar el código en tu ESP32.</li>
    </ul>
  </li>
  <li>
    <p><strong>Monitor Serial</strong>:</p>

    <ul>
      <li>Una vez cargado el programa, si deseas ver salidas seriales o interactuar con tu ESP32, haz clic en el ícono de "Plug" en la barra inferior de PlatformIO para abrir el Monitor Serial.</li>
    </ul>
  </li>
  <li>
    <p><strong>Bibliotecas y Dependencias</strong>:</p>

    <ul>
      <li>PlatformIO facilita la gestión de bibliotecas. En la página de inicio de PlatformIO, puedes buscar bibliotecas y agregarlas a tu proyecto fácilmente.</li>
    </ul>
  </li>
</ol>

<p>Es un resumen breve, debes aprender a usar el archivo platformio.ini, y cosas que en un simple post no puedo explicarte, pero hay muchos videos de youtube.</p>

<h2 id="heading-delay-vs-millis">Delay vs Millis</h2>

<p>La función <code class="language-plaintext highlighter-rouge">delay()</code> es una función proporcionada por el framework de Arduino para pausar la ejecución del programa durante un número especificado de milisegundos. Por ejemplo, delay(1000) pausará la ejecución durante 1 segundo. 1 segundo todo el programa, absolutamente todo. ¿Eso es lo que quieres?</p>

<p><strong>Limitaciones de <code class="language-plaintext highlighter-rouge">delay()</code>:</strong></p>

<ul>
  <li><strong>Bloqueo</strong>: Durante el tiempo que delay() está en ejecución, el microcontrolador no hace nada más. Esto significa que tu programa no puede responder a entradas externas ni realizar otras tareas en paralelo.</li>
  <li><strong>Ineficiencia</strong>: El uso de delay() en aplicaciones que requieren multitarea puede hacer que tu código sea ineficiente o que funcione de manera errática.</li>
  <li><strong>Inflexibilidad</strong>: Si utilizas delay() en múltiples partes de tu código, cambiar el tiempo de pausa puede ser complicado y podría afectar otras funcionalidades.</li>
</ul>

<p><strong>Solución con <code class="language-plaintext highlighter-rouge">millis()</code>:</strong> La función millis() devuelve el número de milisegundos desde que el programa Arduino comenzó a ejecutarse. Esto puede usarse para crear intervalos de tiempo no bloqueantes.</p>

<p><strong>Ejemplo práctico con millis():</strong> Supongamos que quieres hacer parpadear un LED cada segundo, pero al mismo tiempo, deseas verificar constantemente un botón para encender o apagar otro LED.</p>

<p>Sin millis(), podrías tener tentación de usar delay() para el parpadeo, pero eso bloquearía la capacidad de detectar el botón. Con millis(), puedes hacer ambas cosas simultáneamente:</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="kt">int</span> <span class="n">ledPin</span> <span class="o">=</span> <span class="mi">13</span><span class="p">;</span>        <span class="c1">// Pin del LED parpadeante.</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">buttonPin</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>      <span class="c1">// Pin del botón.</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">otherLedPin</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>   <span class="c1">// Pin del otro LED.</span>

<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">previousMillis</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
<span class="k">const</span> <span class="kt">long</span> <span class="n">interval</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span>  <span class="c1">// Intervalo para parpadear (1 segundo).</span>

<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">pinMode</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
  <span class="n">pinMode</span><span class="p">(</span><span class="n">buttonPin</span><span class="p">,</span> <span class="n">INPUT</span><span class="p">);</span>
  <span class="n">pinMode</span><span class="p">(</span><span class="n">otherLedPin</span><span class="p">,</span> <span class="n">OUTPUT</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">loop</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">currentMillis</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span>

  <span class="c1">// Parpadeo del LED sin bloquear:</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">currentMillis</span> <span class="o">-</span> <span class="n">previousMillis</span> <span class="o">&gt;=</span> <span class="n">interval</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">previousMillis</span> <span class="o">=</span> <span class="n">currentMillis</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">digitalRead</span><span class="p">(</span><span class="n">ledPin</span><span class="p">)</span> <span class="o">==</span> <span class="n">LOW</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">digitalWrite</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>  <span class="c1">// Enciende el LED.</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">digitalWrite</span><span class="p">(</span><span class="n">ledPin</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>   <span class="c1">// Apaga el LED.</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// Comprobación del botón:</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">digitalRead</span><span class="p">(</span><span class="n">buttonPin</span><span class="p">)</span> <span class="o">==</span> <span class="n">HIGH</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">digitalWrite</span><span class="p">(</span><span class="n">otherLedPin</span><span class="p">,</span> <span class="n">HIGH</span><span class="p">);</span>   <span class="c1">// Enciende el otro LED.</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">digitalWrite</span><span class="p">(</span><span class="n">otherLedPin</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span>    <span class="c1">// Apaga el otro LED.</span>
  <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h3 id="heading-qué-comprar">¿Qué comprar?</h3>

<p>A la hora de ponerte a buscar que comprar, te inquieta ver la cantidad de dispositivos, con sus respectivas configuraciones, esp32 si, pero con pantalla, sin pantalla, con gps, con sonido, con batería, con usb tipo c o no, …</p>

<p>Los kits como <strong>Freenove, TTGO, LilyGO y M5Stack</strong>, ofrecen beneficios adicionales, como pantallas integradas, altavoces, fácil acceso a pines para sensores y protecciones adicionales contra cortocircuitos. Estos componentes adicionales pueden hacer que la experiencia de desarrollo sea más intuitiva y segura, especialmente para aquellos que no tienen mucha experiencia en electrónica. Sin embargo, es importante investigar las especificaciones y características de cada kit para asegurarte de que se ajuste a tus necesidades.</p>

<p>Si estás indeciso, y no te importa mucho el dinero apuesta por M5stack. No solo son sus dispositivos. Han creado una linea de librerías, todas comentadas en GitHub que te llevarán la electrónica a otro mundo. Su precio es elevado pero su calidad es suprema.</p>

<p>El resto tiene precios razonables, que pueden triplicar el precio de un esp32, pero que aún así merece la pena, ya que cuentan casi siempre de más memoria, y mejor calidad.</p>

<p>Ten en cuenta que muchas tiendas de Aliexpress si compras esp32 normales te los pueden vender sin soldar, si no cuentas con soldador te puede generar un impedimento extra.</p>

<p>Ten cuidado que ponga esp32 y no esp8266, ya que esta es una vs anterior, y no cuenta con todo lo que tiene esp32, aunque en muchas cosas es compatible, no todo. Si tiras por lo barato, todos los caminos te llebarán a él.</p>

<p>Todos los que he mencionado cuentan con cuenta GitHub, con una lista muy grande de ejemplos de ejecutar directamente.</p>

<p>Evita comprar kits de arduino, o raspberry, estos no son 100% compatibles, ya que funcionana a 5v, y esp32 solo su conexión, el resto de los pines para conectar sensores funcionan a 3.3v.  Ten por seguro que a tu esp32 le saldría humo y a tirar.</p>

<p>No te lances a comprar como loco todo lo que ves. Compra lo justo, junto con unos sensores que te vendan esas empresas, para usar sus ejemplos.</p>

<h2 id="heading-programar-en-vue-react-angular-con-esp32-websockets-y-bluetooth">Programar en Vue, React, Angular con esp32 (websockets y bluetooth)</h2>

<ul>
  <li>
    <p>Si bien no puedes ejecutar Angular, React o Vue directamente en un ESP32, puedes construir una aplicación web con cualquiera de estos frameworks que se comunique con el ESP32.</p>
  </li>
  <li>
    <p>Por ejemplo, podrías tener una aplicación web en Angular que utiliza WebSockets para comunicarse con un servidor que, a su vez, se comunica con el ESP32. O podrías tener una aplicación que se conecta directamente al ESP32 a través de Bluetooth utilizando alguna API de navegadores web modernos.</p>
  </li>
  <li>
    <p>El ESP32 tiene capacidad Bluetooth y BLE (Bluetooth de baja energía). Con el SDK adecuado, puedes programar el ESP32 para que actúe como un dispositivo Bluetooth que pueda comunicarse con otras plataformas, incluidos navegadores web que admitan API de Bluetooth.</p>
  </li>
  <li>
    <p>WebSockets permite una comunicación bidireccional entre un cliente web y un servidor. Podrías programar el ESP32 para que actúe como un servidor WebSocket y permita que las aplicaciones web se comuniquen con él en tiempo real.</p>
  </li>
  <li>
    <p>En el Arduino o ESP32, programarás la lógica que necesitas para interactuar con la interfaz web. Por ejemplo, si desde la interfaz decides encender un LED, el microcontrolador recibirá ese comando y encenderá el LED.</p>
  </li>
</ul>

<h2 id="heading-consejos-de-electrónica">Consejos de electrónica</h2>

<ul>
  <li>Etiqueta qué es , mételo en una bolsa, no lo pegues, solo dejaló ahí, para indicar el nombre del sensor, el voltaje que usa.</li>
  <li>Lee los datasheet, no todos valen, deberás buscar</li>
  <li>Si te lanzas a crear pcbs, aprende kicad: es gratis y profesional. Para imprimir, por 4 euros 5 tarjetas en jlcpcb o pcway</li>
  <li>Si sale humo blanco, u otro color, desconecta y a la basura, no intentes hacer nada por revivirlo, te puedes cargar tu pc.</li>
  <li>No sobrecargues de sensores tu proyecto, hazlo fácil. Además, recuerda que tienes que contar los amperios  que introduces, sino habrá daños irreparables.</li>
  <li>No dejes tus proyectos en protoboard, aprende a soldar, no hace falta comprarte muchas cosas, yo empecé con un soldador de amazon de 20 euros. ahora tengo el ts101.</li>
  <li>Si sueldas compra un aspirador (ventilador al revés), estaño sin plomo (tu vida cuesta más, págalo). Usa gafas de protección, no dejes mucho tiempo encendido el soldador y apágalo si no lo usas.</li>
  <li>No te metas directamente con baterías. hay muchas constantes que aún no entiendes.</li>
  <li>compra varios maletines de herramientas, con cada departamento una hojita de qué es, voltaje. Información que sepas.</li>
  <li>compra en aliexpress. Las esperas son largas, si, pero a mitad de precio. No todas las tiendas son fiables, mira las de mayor número de venta, las oficiales, las que les escribes preguntando por su datasheet y te contestan bien.</li>
  <li>Empieza a soldar y desoldar pin header, leds, cosas baratas.</li>
  <li>Cada sensor, componente cuenta con un módulo que ocupa más, es más feo, pero te ayuda a no saber electrónica, te da todo lo que necesita, como puedría ser condensadores, resistencias, mosfet.</li>
  <li>Compra un multímetro digital, nada de analógico, automático.</li>
</ul>

<h2 id="heading-libros-recomendados">Libros recomendados</h2>

<h3 id="heading-electrónica">Electrónica:</h3>

<ul>
  <li>
    <p>Electrónica para makers - guía completa. De paolo Aliverti</p>
  </li>
  <li>
    <p>Learn Electronics with Arduino An Illustrated Beginner's Guide to Physical Computing - Jody Culkin, Eric Hagan</p>
  </li>
</ul>

<h3 id="heading-arduino-esp32">Arduino-esp32:</h3>

<ul>
  <li>Todos los tutoriales de Rui Santos y Sara Santos. (tienen muchísimos, de todo). También tienen de micropython.</li>
</ul>

<h3 id="heading-webs">webs</h3>

<ul>
  <li>https://www.luisllamas.es/</li>
  <li>https://randomnerdtutorials.com/</li>
  <li>https://docs.arduino.cc/learn</li>
</ul>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Microcontroladores" /><summary type="html"><![CDATA[Consejos y guía de compra: esp32]]></summary></entry><entry><title type="html">Angular - ngTemplateOutlet - directiva de @angular/commons</title><link href="https://jolugama.com/blog/2021/07/19/angular-ngtemplateoutlet/" rel="alternate" type="text/html" title="Angular - ngTemplateOutlet - directiva de @angular/commons" /><published>2021-07-19T06:12:00+00:00</published><updated>2021-07-19T06:12:00+00:00</updated><id>https://jolugama.com/blog/2021/07/19/angular-ngtemplateoutlet</id><content type="html" xml:base="https://jolugama.com/blog/2021/07/19/angular-ngtemplateoutlet/"><![CDATA[<h1 id="heading-ngtemplateoutlet---directiva-de-angularcommons">ngTemplateOutlet - directiva de @angular/commons</h1>

<h2 id="heading-usos">Usos</h2>

<blockquote>
  <p>La directiva NgTemplateOutlet de Angular es una herramienta poderosa que te permite crear contenido dinámico y reutilizable en tus aplicaciones. Se utiliza principalmente para insertar una plantilla (template) dentro de la vista, y puede recibir información adicional mediante el paso de contexto. Aquí tienes una lista de algunas cosas que puedes hacer con NgTemplateOutlet:</p>
</blockquote>

<ol>
  <li>Crear contenido reutilizable: Puedes utilizar NgTemplateOutlet para definir fragmentos de código HTML que se pueden reutilizar en varias partes de tu aplicación, lo que facilita la gestión y el mantenimiento del código.</li>
  <li>Generar contenido dinámico: NgTemplateOutlet permite insertar una plantilla en diferentes partes de la aplicación en función de ciertas condiciones o eventos, lo que te permite adaptar el contenido mostrado a las necesidades específicas del usuario o del contexto.</li>
  <li>Personalizar componentes: Puedes utilizar NgTemplateOutlet para proporcionar plantillas personalizadas a un componente, lo que te permite personalizar su apariencia y comportamiento sin tener que modificar el código del componente en sí.</li>
  <li>Crear listas y tablas dinámicas: Puedes utilizar NgTemplateOutlet junto con otras directivas, como *ngFor, para crear listas o tablas que muestren contenido dinámico en función de los datos proporcionados.</li>
  <li>Implementar patrones de diseño: NgTemplateOutlet puede ser útil para implementar patrones de diseño como el patrón de transclusión de contenido, que permite proyectar contenido de un componente a otro.</li>
  <li>Pasar el contexto: Con la ayuda de la directiva NgTemplateOutlet, puedes pasar el contexto que incluye datos adicionales a la plantilla, lo que te permite utilizar esos datos para personalizar la apariencia o el comportamiento del contenido generado.</li>
  <li>Crear vistas anidadas: Puedes utilizar NgTemplateOutlet para crear vistas anidadas, lo que facilita la organización y la estructuración de aplicaciones más grandes y complejas.</li>
  <li>Mejorar la performance: Al utilizar NgTemplateOutlet para generar contenido dinámico solo cuando es necesario, puedes mejorar el rendimiento de tu aplicación al reducir la cantidad de elementos del DOM y los enlaces de datos que deben gestionarse en tiempo de ejecución.</li>
</ol>

<p>Para utilizar NgTemplateOutlet en tu aplicación Angular, simplemente debes agregar la directiva ngTemplateOutlet al elemento del DOM donde deseas insertar la plantilla, y proporcionar la referencia a la plantilla que quieres utilizar.</p>

<h2 id="heading-ejemplo-básico---contenido-reutilizable">Ejemplo básico - contenido reutilizable</h2>

<ol>
  <li>Primero, crea un componente Angular y define una plantilla utilizando la etiqueta <code class="language-plaintext highlighter-rouge">&lt;ng-template&gt;</code>:</li>
</ol>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- app.component.html --&gt;</span>
<span class="nt">&lt;div&gt;</span>
  <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"miTemplate; context: miContexto"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#miTemplate</span> <span class="na">let-mensaje=</span><span class="s">"mensaje"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;p&gt;&lt;/p&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<ol>
  <li>Luego, en el archivo TypeScript del componente, define el contexto que deseas pasar a la plantilla:</li>
</ol>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">// app.component.ts</span>
  <span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

  <span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
   <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-root</span><span class="dl">'</span><span class="p">,</span>
   <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./app.component.html</span><span class="dl">'</span><span class="p">,</span>
   <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./app.component.scss</span><span class="dl">'</span><span class="p">],</span>
  <span class="p">})</span>
  <span class="k">export</span> <span class="kd">class</span> <span class="nx">AppComponent</span> <span class="p">{</span>
   <span class="nx">miContexto</span> <span class="o">=</span> <span class="p">{</span>
     <span class="na">mensaje</span><span class="p">:</span> <span class="dl">'</span><span class="s1">¡Hola, mundo!</span><span class="dl">'</span><span class="p">,</span>
   <span class="p">};</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado una plantilla llamada <code class="language-plaintext highlighter-rouge">miTemplate</code> que muestra un mensaje. La directiva <code class="language-plaintext highlighter-rouge">*ngTemplateOutlet</code> se utiliza para insertar la plantilla <code class="language-plaintext highlighter-rouge">miTemplate</code> dentro de un contenedor <code class="language-plaintext highlighter-rouge">&lt;ng-container&gt;</code>. Además, hemos pasado el contexto <code class="language-plaintext highlighter-rouge">miContexto</code> a la plantilla, que contiene un mensaje que se muestra en el párrafo <code class="language-plaintext highlighter-rouge">&lt;p&gt;</code>.</p>

<p>Este ejemplo es bastante simple, pero puedes ampliarlo y combinarlo con otras características y directivas de Angular para crear contenido más dinámico y reutilizable.</p>

<h2 id="heading-ejemplo---contenido-dinámico">Ejemplo - contenido dinámico</h2>

<p>En este ejemplo, vamos a crear una lista de usuarios con información básica y un botón para mostrar más detalles de cada usuario. Cuando se haga clic en el botón, se mostrará una tarjeta con la información adicional del usuario.</p>

<ul>
  <li>Primero, crea el componente Angular y define las plantillas en el archivo HTML:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- user-list.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">*ngFor=</span><span class="s">"let user of users"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h3&gt;&lt;/h3&gt;</span>
  <span class="nt">&lt;p&gt;&lt;/p&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">(click)=</span><span class="s">"selectedUser = selectedUser === user ? null : user"</span><span class="nt">&gt;</span>Mostrar detalles<span class="nt">&lt;/button&gt;</span>

  <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"userDetails; context: { user: selectedUser === user ? user : null }"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#userDetails</span> <span class="na">let-user=</span><span class="s">"user"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ng-container</span> <span class="na">*ngIf=</span><span class="s">"user"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"user-details"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;p&gt;</span>Dirección: <span class="nt">&lt;/p&gt;</span>
      <span class="nt">&lt;p&gt;</span>Teléfono: <span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/ng-container&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el archivo TypeScript del componente, define los datos de los usuarios y la variable <code class="language-plaintext highlighter-rouge">selectedUser</code>:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// user-list.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-user-list</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./user-list.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./user-list.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">UserListComponent</span> <span class="p">{</span>
  <span class="nx">users</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Juan Pérez</span><span class="dl">'</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="dl">'</span><span class="s1">juan@example.com</span><span class="dl">'</span><span class="p">,</span> <span class="na">address</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Calle 123</span><span class="dl">'</span><span class="p">,</span> <span class="na">phone</span><span class="p">:</span> <span class="dl">'</span><span class="s1">555-1234</span><span class="dl">'</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">María Rodríguez</span><span class="dl">'</span><span class="p">,</span> <span class="na">email</span><span class="p">:</span> <span class="dl">'</span><span class="s1">maria@example.com</span><span class="dl">'</span><span class="p">,</span> <span class="na">address</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Calle 456</span><span class="dl">'</span><span class="p">,</span> <span class="na">phone</span><span class="p">:</span> <span class="dl">'</span><span class="s1">555-5678</span><span class="dl">'</span> <span class="p">},</span>
    <span class="c1">// ...</span>
  <span class="p">];</span>

  <span class="nx">selectedUser</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado un componente llamado <code class="language-plaintext highlighter-rouge">UserListComponent</code> que muestra una lista de usuarios y utiliza la directiva <code class="language-plaintext highlighter-rouge">*ngFor</code> para iterar sobre el arreglo <code class="language-plaintext highlighter-rouge">users</code>. Para cada usuario, se muestra su nombre, correo electrónico y un botón para mostrar más detalles.</p>

<p>Cuando se hace clic en el botón "Mostrar detalles", la variable <code class="language-plaintext highlighter-rouge">selectedUser</code> se actualiza con el usuario seleccionado. La directiva <code class="language-plaintext highlighter-rouge">*ngTemplateOutlet</code> se utiliza para insertar la plantilla <code class="language-plaintext highlighter-rouge">userDetails</code> en la vista y pasar el contexto que contiene el usuario seleccionado.</p>

<p>La plantilla <code class="language-plaintext highlighter-rouge">userDetails</code> muestra la información adicional del usuario (dirección y teléfono) solo si el usuario seleccionado coincide con el usuario actual de la iteración. De lo contrario, no se muestra nada.</p>

<h2 id="heading-ejemplo---personalizando-un-componente">Ejemplo - personalizando un componente</h2>

<ul>
  <li>Primero, crea el componente de tarjeta genérica y define la plantilla en el archivo HTML:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- card.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h3</span> <span class="na">class=</span><span class="s">"card-title"</span><span class="nt">&gt;&lt;/h3&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card-content"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"contentTemplate"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el archivo TypeScript del componente, define las entradas para el título y la plantilla de contenido:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// card.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span><span class="p">,</span> <span class="nx">Input</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-card</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./card.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./card.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">CardComponent</span> <span class="p">{</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">title</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">contentTemplate</span><span class="p">:</span> <span class="nx">TemplateRef</span><span class="o">&lt;</span><span class="kr">any</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Ahora, en otro componente (por ejemplo, AppComponent), utiliza el componente de tarjeta y proporciona una plantilla personalizada para cambiar su apariencia y comportamiento:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- app.component.html --&gt;</span>
<span class="nt">&lt;app-card</span> <span class="na">[title]=</span><span class="s">"'Tarjeta personalizada'"</span> <span class="na">[contentTemplate]=</span><span class="s">"customCardTemplate"</span><span class="nt">&gt;&lt;/app-card&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#customCardTemplate</span><span class="nt">&gt;</span>
  <span class="nt">&lt;p&gt;</span>Este es el contenido personalizado de la tarjeta.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">(click)=</span><span class="s">"onButtonClick()"</span><span class="nt">&gt;</span>Presiona aquí<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<ul>
  <li>En el archivo TypeScript del componente que utiliza la tarjeta, define el comportamiento personalizado (en este caso, un método para manejar el clic del botón):</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-root</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./app.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./app.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppComponent</span> <span class="p">{</span>
  <span class="nx">onButtonClick</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Botón personalizado presionado!</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado un componente genérico de tarjeta llamado <code class="language-plaintext highlighter-rouge">CardComponent</code> que acepta un título y una plantilla de contenido como entradas. La plantilla de contenido se inserta en la tarjeta utilizando la directiva <code class="language-plaintext highlighter-rouge">*ngTemplateOutlet</code>.</p>

<p>El componente <code class="language-plaintext highlighter-rouge">AppComponent</code> utiliza el componente de tarjeta y proporciona una plantilla personalizada llamada <code class="language-plaintext highlighter-rouge">customCardTemplate</code> para cambiar la apariencia y el comportamiento de la tarjeta. La plantilla personalizada incluye un párrafo y un botón con un evento de clic que llama al método <code class="language-plaintext highlighter-rouge">onButtonClick</code> en el componente <code class="language-plaintext highlighter-rouge">AppComponent</code>.</p>

<h2 id="heading-ejemplo---crear-tabla-dinámica-con-contenido-dinámico">Ejemplo - Crear tabla dinámica con contenido dinámico</h2>

<ul>
  <li>Primero, crea el componente y define las plantillas en el archivo HTML:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- product-table.component.html --&gt;</span>
<span class="nt">&lt;table&gt;</span>
  <span class="nt">&lt;thead&gt;</span>
    <span class="nt">&lt;tr&gt;</span>
      <span class="nt">&lt;th&gt;</span>#<span class="nt">&lt;/th&gt;</span>
      <span class="nt">&lt;th&gt;</span>Nombre del producto<span class="nt">&lt;/th&gt;</span>
      <span class="nt">&lt;th&gt;</span>Precio<span class="nt">&lt;/th&gt;</span>
    <span class="nt">&lt;/tr&gt;</span>
  <span class="nt">&lt;/thead&gt;</span>
  <span class="nt">&lt;tbody&gt;</span>
    <span class="nt">&lt;ng-container</span> <span class="na">*ngFor=</span><span class="s">"let product of products; let i = index"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"rowTemplate; context: { $implicit: product, index: i }"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
    <span class="nt">&lt;/ng-container&gt;</span>
  <span class="nt">&lt;/tbody&gt;</span>
<span class="nt">&lt;/table&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#rowTemplate</span> <span class="na">let-product</span> <span class="na">let-i=</span><span class="s">"index"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;tr</span> <span class="na">[class.highlight]=</span><span class="s">"i % 2 === 0"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;td&gt;&lt;/td&gt;</span>
    <span class="nt">&lt;td&gt;&lt;/td&gt;</span>
    <span class="nt">&lt;td&gt;&lt;/td&gt;</span>
  <span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el archivo TypeScript del componente, define los datos de los productos:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// product-table.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-product-table</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./product-table.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./product-table.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">ProductTableComponent</span> <span class="p">{</span>
  <span class="nx">products</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Producto A</span><span class="dl">'</span><span class="p">,</span> <span class="na">price</span><span class="p">:</span> <span class="mi">100</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Producto B</span><span class="dl">'</span><span class="p">,</span> <span class="na">price</span><span class="p">:</span> <span class="mi">200</span> <span class="p">},</span>
    <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Producto C</span><span class="dl">'</span><span class="p">,</span> <span class="na">price</span><span class="p">:</span> <span class="mi">150</span> <span class="p">},</span>
    <span class="c1">// ...</span>
  <span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado un componente llamado <code class="language-plaintext highlighter-rouge">ProductTableComponent</code> que muestra una tabla con información sobre productos y sus precios. Utilizamos la directiva *ngFor para iterar sobre el arreglo <code class="language-plaintext highlighter-rouge">products</code> y crear una fila para cada producto en la tabla.</p>

<p>La directiva *ngTemplateOutlet se utiliza junto con *ngFor para insertar la plantilla <code class="language-plaintext highlighter-rouge">rowTemplate</code> en la tabla para cada producto. Pasamos el contexto que contiene el producto actual y su índice a la plantilla.</p>

<p>En la plantilla <code class="language-plaintext highlighter-rouge">rowTemplate</code>, utilizamos las variables <code class="language-plaintext highlighter-rouge">product</code> e <code class="language-plaintext highlighter-rouge">i</code> (índice) para personalizar el contenido de cada fila. Aplicamos una clase CSS <code class="language-plaintext highlighter-rouge">highlight</code> a las filas pares (índice par) para resaltarlas. También mostramos el número de fila (índice + 1), el nombre del producto y su precio en formato de moneda.</p>

<h2 id="heading-ejemplo---trasclusión-de-contenido">Ejemplo - Trasclusión de contenido</h2>

<ul>
  <li>Primero, crea el componente de tarjeta y define la plantilla en el archivo HTML:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- card.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card"</span> <span class="na">[ngStyle]=</span><span class="s">"{'background-color': backgroundColor}"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h2&gt;&lt;/h2&gt;</span>
  <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"contentTemplate"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el archivo TypeScript del componente de la tarjeta, define las entradas para el título, el color de fondo y la plantilla de contenido:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// card.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span><span class="p">,</span> <span class="nx">Input</span><span class="p">,</span> <span class="nx">TemplateRef</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-card</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./card.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./card.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">CardComponent</span> <span class="p">{</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">title</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">backgroundColor</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">contentTemplate</span><span class="p">:</span> <span class="nx">TemplateRef</span><span class="o">&lt;</span><span class="kr">any</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>A continuación, crea el componente principal (AppComponent) y define la plantilla en el archivo HTML:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- app.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">style=</span><span class="s">"display: flex"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ng-template</span> <span class="na">#content1</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">style=</span><span class="s">"color: red;"</span><span class="nt">&gt;</span>Este es el contenido personalizado con letras rojas.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/ng-template&gt;</span>

  <span class="nt">&lt;ng-template</span> <span class="na">#content2</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">style=</span><span class="s">"color: blue;"</span><span class="nt">&gt;</span>Este es el contenido personalizado con letras azules.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/ng-template&gt;</span>

  <span class="nt">&lt;app-card</span> <span class="na">title=</span><span class="s">"Tarjeta 1"</span> <span class="na">backgroundColor=</span><span class="s">"lightblue"</span> <span class="na">[contentTemplate]=</span><span class="s">"content1"</span><span class="nt">&gt;&lt;/app-card&gt;</span>
  <span class="nt">&lt;app-card</span> <span class="na">title=</span><span class="s">"Tarjeta 2"</span> <span class="na">backgroundColor=</span><span class="s">"lightgreen"</span> <span class="na">[contentTemplate]=</span><span class="s">"content2"</span><span class="nt">&gt;&lt;/app-card&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<ul>
  <li>En el archivo TypeScript del componente principal, no es necesario realizar cambios adicionales:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-root</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./app.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./app.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppComponent</span> <span class="p">{}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado dos componentes: <code class="language-plaintext highlighter-rouge">CardComponent</code> y <code class="language-plaintext highlighter-rouge">AppComponent</code>. El componente <code class="language-plaintext highlighter-rouge">CardComponent</code> es un componente genérico que muestra una tarjeta con un título y contenido personalizado. El componente <code class="language-plaintext highlighter-rouge">AppComponent</code> es el componente principal que contiene dos instancias del componente <code class="language-plaintext highlighter-rouge">CardComponent</code> con diferentes colores de fondo y estilos de letra.</p>

<p>Utilizamos la directiva *ngTemplateOutlet en el componente <code class="language-plaintext highlighter-rouge">CardComponent</code> para insertar el contenido personalizado desde el componente <code class="language-plaintext highlighter-rouge">AppComponent</code>. Las plantillas de contenido se definen dentro de las etiquetas <ng-template> en el archivo HTML del componente `AppComponent` y se pasan como entrada al componente `CardComponent`.</ng-template></p>

<h2 id="heading-ejemplo---trasclusión-con-objeto-en-lugar-de-plantilla">Ejemplo - trasclusión con objeto en lugar de plantilla</h2>

<p>Es como el ejemplo anterior, pero le incorporamos un objeto, más completo.</p>

<ul>
  <li>Primero, crea una interfaz para el objeto ContentData que contendrá la plantilla y el color del texto:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// content-data.interface.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">TemplateRef</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ContentData</span> <span class="p">{</span>
  <span class="nl">template</span><span class="p">:</span> <span class="nx">TemplateRef</span><span class="o">&lt;</span><span class="kr">any</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nl">textColor</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>A continuación, modifica el componente de tarjeta para que acepte un objeto ContentData en lugar de una TemplateRef directamente:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- card.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card"</span> <span class="na">[ngStyle]=</span><span class="s">"{'background-color': backgroundColor}"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h2&gt;&lt;/h2&gt;</span>
  <span class="nt">&lt;ng-container</span> <span class="na">*ngTemplateOutlet=</span><span class="s">"contentData.template; context: {textColor: contentData.textColor}"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el archivo TypeScript del componente de la tarjeta, actualiza la entrada para recibir un objeto ContentData:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// card.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span><span class="p">,</span> <span class="nx">Input</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ContentData</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../interfaces/content-data.interface</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-card</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./card.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./card.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">CardComponent</span> <span class="p">{</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">title</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">backgroundColor</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="p">@</span><span class="nd">Input</span><span class="p">()</span> <span class="nx">contentData</span><span class="p">:</span> <span class="nx">ContentData</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>A continuación, actualiza el componente principal (AppComponent) para pasar un objeto ContentData a cada componente de tarjeta:</li>
</ul>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- app.component.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">style=</span><span class="s">"display: flex"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ng-template</span> <span class="na">#content1</span> <span class="na">let-textColor=</span><span class="s">"textColor"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">[style.color]=</span><span class="s">"textColor"</span><span class="nt">&gt;</span>Este es el contenido personalizado con letras rojas.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/ng-template&gt;</span>

  <span class="nt">&lt;ng-template</span> <span class="na">#content2</span> <span class="na">let-textColor=</span><span class="s">"textColor"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">[style.color]=</span><span class="s">"textColor"</span><span class="nt">&gt;</span>Este es el contenido personalizado con letras azules.<span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;/ng-template&gt;</span>

  <span class="nt">&lt;app-card</span> <span class="na">title=</span><span class="s">"Tarjeta 1"</span> <span class="na">backgroundColor=</span><span class="s">"lightblue"</span> <span class="na">[contentData]=</span><span class="s">"{ template: content1, textColor: 'red' }"</span><span class="nt">&gt;&lt;/app-card&gt;</span>
  <span class="nt">&lt;app-card</span> <span class="na">title=</span><span class="s">"Tarjeta 2"</span> <span class="na">backgroundColor=</span><span class="s">"lightgreen"</span> <span class="na">[contentData]=</span><span class="s">"{ template: content2, textColor: 'blue' }"</span><span class="nt">&gt;&lt;/app-card&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<ul>
  <li>No es necesario realizar cambios adicionales en el archivo TypeScript del componente principal:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-root</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">templateUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./app.component.html</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">styleUrls</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">./app.component.scss</span><span class="dl">'</span><span class="p">],</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppComponent</span> <span class="p">{}</span>
</code></pre></div></div>

<p>En este ejemplo, hemos creado una interfaz <code class="language-plaintext highlighter-rouge">ContentData</code> que contiene una referencia a la plantilla y el color del texto. Luego, ajustamos los componentes <code class="language-plaintext highlighter-rouge">CardComponent</code> y <code class="language-plaintext highlighter-rouge">AppComponent</code> para utilizar esta nueva estructura de datos.</p>

<p>El componente <code class="language-plaintext highlighter-rouge">CardComponent</code> ahora acepta un objeto <code class="language-plaintext highlighter-rouge">ContentData</code> como entrada en lugar de una TemplateRef directamente. En el componente <code class="language-plaintext highlighter-rouge">AppComponent</code>, creamos objetos <code class="language-plaintext highlighter-rouge">ContentData</code> para cada tarjeta y los pasamos como entrada a los componentes <code class="language-plaintext highlighter-rouge">CardComponent</code>.</p>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Angular" /><summary type="html"><![CDATA[Crear contenido dinámico y reutilizable en Angular: ngTemplateOutlet]]></summary></entry><entry><title type="html">Angular - NgComponentOutlet - directiva de @angular/commons</title><link href="https://jolugama.com/blog/2021/07/12/angular-directiva-ngcomponentoutlet/" rel="alternate" type="text/html" title="Angular - NgComponentOutlet - directiva de @angular/commons" /><published>2021-07-12T03:20:00+00:00</published><updated>2021-07-12T03:20:00+00:00</updated><id>https://jolugama.com/blog/2021/07/12/angular-directiva-ngcomponentoutlet</id><content type="html" xml:base="https://jolugama.com/blog/2021/07/12/angular-directiva-ngcomponentoutlet/"><![CDATA[<h1 id="heading-ngcomponentoutlet---directiva-de-angularcommons">NgComponentOutlet - directiva de @angular/commons</h1>

<h2 id="heading-principales-usos">Principales usos</h2>

<blockquote>
  <p>La directiva <code class="language-plaintext highlighter-rouge">NgComponentOutlet</code> de Angular se utiliza para dinámicamente cargar y renderizar componentes en una plantilla Angular. Algunas de las cosas que se pueden hacer con esta directiva son:</p>
</blockquote>

<ol>
  <li>Cargar componentes dinámicamente en función de la lógica de la aplicación.</li>
  <li>Cambiar el componente que se muestra en una vista según la interacción del usuario o los datos obtenidos de una API.</li>
  <li>Crear plantillas genéricas que pueden ser reutilizadas para renderizar diferentes componentes según sea necesario.</li>
  <li>Aprovechar las ventajas del lazy-loading, para cargar componentes solo cuando sea necesario.</li>
  <li>Renderizar componentes en función de la lógica del negocio y del estado de la aplicación.</li>
  <li>Crear componentes genéricos que se pueden utilizar en diferentes partes de la aplicación.</li>
  <li>Implementar lógica de control de acceso para restringir la visibilidad de ciertos componentes en función de los permisos del usuario.</li>
  <li>Crear una experiencia de usuario dinámica y personalizada que cambia según las acciones del usuario o las condiciones de la aplicación.</li>
  <li>Renderizar componentes dentro de una lista o de otro componente dinámicamente.</li>
  <li>Crear componentes modales dinámicos que se cargan y muestran en la pantalla según las necesidades de la aplicación.</li>
</ol>

<h2 id="heading-ejemplo---carga-de-componentes-dinámicamente">Ejemplo - Carga de componentes dinámicamente</h2>

<ul>
  <li>Primero, crea dos componentes de ejemplo que se cargarán dinámicamente:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// hello.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-hello</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="dl">'</span><span class="s1">&lt;p&gt;Hello, World!&lt;/p&gt;</span><span class="dl">'</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">HelloComponent</span> <span class="p">{}</span>

<span class="c1">// goodbye.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-goodbye</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="dl">'</span><span class="s1">&lt;p&gt;Goodbye, World!&lt;/p&gt;</span><span class="dl">'</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">GoodbyeComponent</span> <span class="p">{}</span>
</code></pre></div></div>

<ul>
  <li>Luego, en el componente principal, importa los componentes y define una variable que represente el componente que se cargará de forma dinámica:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.component.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Component</span><span class="p">,</span> <span class="nx">Type</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">HelloComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./hello.component</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">GoodbyeComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./goodbye.component</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">Component</span><span class="p">({</span>
  <span class="na">selector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">app-root</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="s2">`
    &lt;button (click)="switchComponent()"&gt;Switch Component&lt;/button&gt;
    &lt;ng-container *ngComponentOutlet="currentComponent"&gt;&lt;/ng-container&gt;
  `</span><span class="p">,</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppComponent</span> <span class="p">{</span>
  <span class="nl">currentComponent</span><span class="p">:</span> <span class="nx">Type</span><span class="o">&lt;</span><span class="kr">any</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="k">private</span> <span class="nx">isHelloComponent</span><span class="p">:</span> <span class="nx">boolean</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>

  <span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">currentComponent</span> <span class="o">=</span> <span class="nx">HelloComponent</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">switchComponent</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">isHelloComponent</span> <span class="o">=</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">isHelloComponent</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">currentComponent</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">isHelloComponent</span> <span class="p">?</span> <span class="nx">HelloComponent</span> <span class="p">:</span> <span class="nx">GoodbyeComponent</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>No olvides agregar los componentes en las declaraciones y en <code class="language-plaintext highlighter-rouge">entryComponents</code> del módulo principal (si estás utilizando Angular 8 o inferior):</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.module.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">BrowserModule</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/platform-browser</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">NgModule</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@angular/core</span><span class="dl">'</span><span class="p">;</span>

<span class="k">import</span> <span class="p">{</span> <span class="nx">AppComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./app.component</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">HelloComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./hello.component</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">GoodbyeComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./goodbye.component</span><span class="dl">'</span><span class="p">;</span>

<span class="p">@</span><span class="nd">NgModule</span><span class="p">({</span>
  <span class="na">declarations</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">AppComponent</span><span class="p">,</span>
    <span class="nx">HelloComponent</span><span class="p">,</span>
    <span class="nx">GoodbyeComponent</span>
  <span class="p">],</span>
  <span class="na">imports</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">BrowserModule</span>
  <span class="p">],</span>
  <span class="na">providers</span><span class="p">:</span> <span class="p">[],</span>
  <span class="na">bootstrap</span><span class="p">:</span> <span class="p">[</span><span class="nx">AppComponent</span><span class="p">],</span>
  <span class="na">entryComponents</span><span class="p">:</span> <span class="p">[</span><span class="nx">HelloComponent</span><span class="p">,</span> <span class="nx">GoodbyeComponent</span><span class="p">]</span>
<span class="p">})</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppModule</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>

<p>En este ejemplo, se crea un botón que, al hacer clic, cambia el componente que se muestra en el contenedor usando la directiva <code class="language-plaintext highlighter-rouge">ngComponentOutlet</code>. Los componentes <code class="language-plaintext highlighter-rouge">HelloComponent</code> y <code class="language-plaintext highlighter-rouge">GoodbyeComponent</code> se cargarán de forma dinámica en función de la lógica de la aplicación, en este caso, alternándolos cada vez que se hace clic en el botón.</p>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Angular" /><summary type="html"><![CDATA[para dinámicamente cargar y renderizar componentes en una plantilla Angular]]></summary></entry><entry><title type="html">PWA - Aplicaciones Web Progresivas 1/2</title><link href="https://jolugama.com/blog/2021/05/27/PWA-aplicaciones-web-progresivas/" rel="alternate" type="text/html" title="PWA - Aplicaciones Web Progresivas 1/2" /><published>2021-05-27T07:00:00+00:00</published><updated>2021-05-27T07:00:00+00:00</updated><id>https://jolugama.com/blog/2021/05/27/PWA-aplicaciones-web-progresivas</id><content type="html" xml:base="https://jolugama.com/blog/2021/05/27/PWA-aplicaciones-web-progresivas/"><![CDATA[<!-- # PWA - Aplicaciones web progresivas -->
<blockquote>
  <p>Tutorial, consejos y primeros pasos para el desarrollo de PWA. Doy ideas muy generales y básicas, que considero esenciales en la realizacion de las mismas. En el siguiente post (2/2) se pasa a la práctica con <code class="language-plaintext highlighter-rouge">Workbox</code>.</p>
</blockquote>

<p>Aplicación web creado en html, css y javascript principalmente, que se abre en navegador, capaz de trabajar sin conexión, para móvil o escritorio, optimiza la carga de la web con sus distintas estrategias de cacheado. Creado en 2015 por google.</p>

<ul>
  <li>Ejemplo de PWA en <a href="https://jolugama.github.io/miPWA/index.html">jolugama.github.io/miPWA/index.html</a></li>
  <li><a href="https://github.com/jolugama/miPWA">github con las fuentes</a></li>
  <li>Esta web es una PWA tambien!!!!!</li>
</ul>

<div id="toc"></div>

<h2 id="heading-características">Características</h2>

<ul>
  <li><strong>Notificaciones push</strong>: Es una funcionalidad que hasta hace poco solo tenían las app nativas e híbridas(ionic).</li>
  <li><strong>Cache (offline)</strong>: Posibilidad de poder cachear ciertas partes de la web.</li>
  <li><strong>Multiplataforma</strong>: Se abre desde un navegador, tanto móvil como desktop.</li>
  <li><strong>IndexedDB</strong>: Usa la bbdd del propio navegador, haciendo persistente mucha información.</li>
  <li><strong>Apariencia de app nativa</strong>: Ocultándose las barras de herramientas y búsqueda que suele tener los navegadores, junto con un css trabajado aparte, da una apariencia 100% nativa, aunque no está obligado a hacerlo.</li>
  <li><strong>Acceso directo</strong>:Se puede añadir como un icono en el escritorio del pc, o del móvil, haciendo imposible detectar que es una web o PWA, o una app nativa. Para ello hay que pulsar el botón de instalar aplicación o el mismo app mandar popup para ello.</li>
  <li><strong>HTTPS</strong>: Solo funciona en localhost y https, web seguras.</li>
  <li><strong>Independencia de Markets</strong>: No depende de ninguna market, pudiendo estar en un dominio web, con el sistema de posicionamiento que tiene cualquier web, y encontrado desde los buscadores de la misma manera.</li>
  <li><strong>Manejo de Actualizaciones</strong>: El navegador corre en background en el movil, y dispara un evento modal donde indica actualización.</li>
</ul>

<p>Si quieres saber que navegadores lo soportan:
<a href="https://caniuse.com/#search=service%20worker">can I use service workers</a></p>

<p>Si estás en un internet explorer que no sea edge, no va a funcionar, para el resto, muy seguro que si.</p>

<blockquote>
  <p>Un problema que veo a PWA es que está en continuo cambio, las webs oficiales de google cambian constantemente, siempre están con carteles de que no está actualizado y está pendiente de hacerlo.</p>
</blockquote>

<h2 id="heading-service-worker">Service worker</h2>

<p>Pertenece a navigator <code class="language-plaintext highlighter-rouge">navigator.serviceWorker</code>, lo tienen incorporado ya todos los navegadores modernos tanto de móvil como escritorio.
Es un proxy programable, controlando las solicitudes de red de tu web.</p>

<h2 id="heading-escuchando-eventos-y-ciclo-de-vida-del-service-worker">Escuchando eventos y ciclo de vida del service worker</h2>

<ul>
  <li>El archivo service worker debe estar en la carpeta raiz, para que pueda controlar todos los archivos. Si algún recurso no está en un hijo de donde se aloje sw.js, esté no podrá cachearlos.</li>
  <li>El evento <code class="language-plaintext highlighter-rouge">install</code> es el primero que obtiene un service worker y solo sucede una vez.</li>
  <li>Una promesa que se pasa a <code class="language-plaintext highlighter-rouge">installEvent.waitUntil()</code> señala la duración y el éxito o fracaso de tu instalación.</li>
  <li>Un service worker no recibirá eventos como <code class="language-plaintext highlighter-rouge">fetch</code> y <code class="language-plaintext highlighter-rouge">push</code> hasta que se termine de instalar correctamente y su estado sea "activo".</li>
  <li>De manera predeterminada, los <code class="language-plaintext highlighter-rouge">fetch</code> de una página no atravesarán un service worker a menos que la solicitud de la página en sí lo haya hecho. Por lo tanto, tendrás que actualizar la página para ver los efectos del service worker.</li>
  <li><code class="language-plaintext highlighter-rouge">clients.claim()</code> puede anular esta configuración predeterminada y tomar el control de las páginas no supervisadas.</li>
  <li>Cuando realizas una llamada a <code class="language-plaintext highlighter-rouge">.register()</code>, se descarga el primer service worker. Si tu secuencia de comandos no se descarga, no se analiza o arroja un error en su ejecución inicial, se rechaza la promesa de registro y se descarta el service worker.
-<code class="language-plaintext highlighter-rouge">self</code> hace referencia al propio service worker.</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">Register</code> –&gt; <code class="language-plaintext highlighter-rouge">install</code> –&gt; (<code class="language-plaintext highlighter-rouge">Error</code> or <code class="language-plaintext highlighter-rouge">Activated</code>) –&gt; <code class="language-plaintext highlighter-rouge">Idle</code> –&gt;(<code class="language-plaintext highlighter-rouge">Active</code> or <code class="language-plaintext highlighter-rouge">Terminated</code>)</p>

<h3 id="heading-register">Register</h3>
<p>Se indica a serviceWorker donde está el archivo. Solo se ejecutará una vez si el navegador no lo tiene registrado. Si se borra cache, o cambia de navegador, se volverá a ejecutar para proceder a su instalación local.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">;</span>
<span class="c1">// para producción</span>
<span class="kd">var</span> <span class="nx">swLocation</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/miwpa/sw.js</span><span class="dl">'</span><span class="p">;</span> 
<span class="c1">// si estamos en localhost</span>
<span class="k">if</span> <span class="p">(</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span> <span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span> <span class="nx">url</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">localhost</span><span class="dl">'</span><span class="p">)</span> <span class="p">)</span> <span class="p">{</span>
        <span class="nx">swLocation</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/sw.js</span><span class="dl">'</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">register</span><span class="p">(</span> <span class="nx">swLocation</span> <span class="p">);</span>
</code></pre></div></div>

<h3 id="heading-install">Install</h3>
<p>El primer evento que recibe un service worker es install. se va a disparar cada la primera vez, y cada vez que haya cambios en ese listener. Pero aún no se activa.</p>

<p>Es el lugar adecuado para almacenar en caché todo lo que necesitas para poder controlar los clientes. La promesa que pasas a event.waitUntil() permite que el navegador sepa que la instalación se completó correctamente.</p>

<p>Si se rechaza la promesa, significa que no se completó la instalación y el navegador elimina el service worker.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ejemplo de evento install</span>
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">install</span><span class="dl">'</span><span class="p">,</span> <span class="nx">e</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="p">...</span>
    <span class="nx">e</span><span class="p">.</span><span class="nx">skipWaiting</span><span class="p">()</span> <span class="c1">// opcional. No recomendable ya que hace saltárselo sin esperas.</span>
    <span class="nx">e</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span> <span class="nx">promise1</span><span class="p">,</span> <span class="nx">promise2</span> <span class="p">])</span>  <span class="p">);</span> <span class="c1">// opcional</span>
<span class="p">});</span>
</code></pre></div></div>

<h3 id="heading-activated">Activated</h3>
<p>Una vez que tu service worker esté listo para controlar clientes y administrar eventos funcionales como <code class="language-plaintext highlighter-rouge">push</code> y <code class="language-plaintext highlighter-rouge">sync</code>, recibirás un evento <code class="language-plaintext highlighter-rouge">activate</code>. Sin embargo, eso no significa que se controlará la página desde la que se realizó la llamada a .register().</p>

<p>La primera vez que cargas la versión, no se procesa la solicitud. La configuración predeterminada es consistencia: si tu página se carga sin un service worker, tampoco lo harán los subrecursos. Si cargas la versión otra vez (en otras palabras, si actualizas la página), se controlará la solicitud. Tanto la página como la imagen atravesarán eventos <code class="language-plaintext highlighter-rouge">fetch</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ejemplo de evento activate</span>
<span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">activate</span><span class="dl">'</span><span class="p">,</span> <span class="nx">e</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">respuesta</span> <span class="o">=</span> <span class="nx">caches</span><span class="p">.</span><span class="nx">keys</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span> <span class="nx">keys</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">keys</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span> <span class="nx">key</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="p">...</span>
        <span class="p">});</span>
    <span class="p">});</span>
    <span class="nx">e</span><span class="p">.</span><span class="nx">waitUntil</span><span class="p">(</span> <span class="nx">respuesta</span> <span class="p">);</span> <span class="c1">// opcional</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-fetch">Fetch</h3>
<p>Todo archivo que se cargue al html pasa por fetch, por lo que se utiliza para aceptar, denegar, cambiar cualquier archivo por otro.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">self</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">fetch</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="c1">//fetch event handler</span>
<span class="p">});</span>
</code></pre></div></div>

<h2 id="heading-fundamentos-promesas">Fundamentos: promesas</h2>

<p>Para usar correctamente los service workers es necesario tener conocimientos de promesas, de fetch (nueva interfaz para hacer peticiones Http)
Intentando no alargar este tutorial, existen unas web que lo explican muy bien, en castellano y con ejemplos prácticos.</p>

<ul>
  <li>Promesas: crear, unir, manejo de errores, Promise.all y Promise.race
<a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Usar_promesas">Usar_promesas</a></li>
  <li>Fetch: Fecth API, fet, post, cabeceras, cors
<a href="https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Utilizando_Fetch">Utilizando_Fetch</a></li>
</ul>

<h3 id="heading-promesas-en-cadena">Promesas en cadena</h3>
<p>Encadenar funciones asíncronas, que esperen unos a otras, transformándolas en síncronas.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">sumarUno</span><span class="p">(</span><span class="nx">num</span><span class="p">){</span>
    <span class="kd">let</span> <span class="nx">promesa</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span> <span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span><span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">setTimeout</span> <span class="p">(</span> <span class="p">()</span><span class="o">=&gt;</span><span class="p">{</span>
        <span class="nx">resolve</span><span class="p">(</span><span class="nx">num</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="p">},</span><span class="mi">800</span><span class="p">);</span>
    <span class="p">})</span>
    <span class="k">return</span> <span class="nx">promesa</span><span class="p">;</span> 
<span class="p">}</span>

<span class="nx">sumarUno</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">sumarUno</span><span class="p">)</span>  
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">sumarUno</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">sumarUno</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">sumarUno</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">nuevoNumero</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">nuevoNumero</span><span class="p">);</span> <span class="c1">// 10</span>
<span class="p">})</span>
<span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Error en promesa</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-promiseall">Promise.all</h3>

<p>Se ejecutan un array de funciones asyncronas a la vez. Una vez finalizado puede encadenarse otra promesa como resultado.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">sumarLento</span><span class="p">(</span><span class="nx">numero</span><span class="p">){</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span><span class="nx">reject</span><span class="p">)</span><span class="o">=&gt;</span><span class="p">{</span>
        <span class="nx">setTimeout</span><span class="p">(()</span><span class="o">=&gt;</span><span class="p">{</span>
            <span class="nx">resolve</span><span class="p">(</span><span class="nx">numero</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
        <span class="p">},</span><span class="mi">800</span><span class="p">)</span>
    <span class="p">})</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">sumarRapido</span> <span class="o">=</span> <span class="p">(</span><span class="nx">numero</span><span class="p">)</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span><span class="nx">reject</span><span class="p">)</span><span class="o">=&gt;</span><span class="p">{</span>
        <span class="nx">setTimeout</span><span class="p">(()</span><span class="o">=&gt;</span><span class="p">{</span>
            <span class="nx">resolve</span><span class="p">(</span><span class="nx">numero</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
        <span class="p">},</span><span class="mi">300</span><span class="p">)</span>
    <span class="p">})</span>
<span class="p">}</span>


<span class="nb">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span><span class="nx">sumarLento</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span><span class="nx">sumarRapido</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">respuestas</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">respuestas</span><span class="p">);</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-promiserace">Promise.race</h3>

<p>Solo muestra 1 resultado, el que primero llegue. En este caso será sumarRapido.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Promise</span><span class="p">.</span><span class="nx">race</span><span class="p">(</span><span class="nx">sumarLento</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span><span class="nx">sumarRapido</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">respuestas</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">respuestas</span><span class="p">);</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-fetch-api">Fetch API</h3>

<p>Permite recuperar recursos a través de la red. Javascript ha progresado en este sentido, ya que antes era tan complicado que se solía usar siempre jquery. 
Los frameworks han realizado sus propias versiones, como angularjs y angular 2x con http de httpClient.</p>

<p><strong>Vs antigua de js</strong> (no usar)</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">request</span><span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">https://reqres.in/api/users</span><span class="dl">'</span><span class="p">,</span><span class="kc">true</span><span class="p">);</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
<span class="nx">request</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">state</span><span class="p">){</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">readyState</span><span class="o">===</span><span class="mi">4</span><span class="p">){</span>
        <span class="kd">const</span> <span class="nx">resp</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">response</span><span class="p">);</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">resp</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Vs actual</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://reqres.in/api/users</span><span class="dl">'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">resp</span> <span class="o">=&gt;</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">respObj</span><span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">respObj</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">respObj</span><span class="p">.</span><span class="nx">page</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">respObj</span><span class="p">.</span><span class="nx">per_page</span><span class="p">);</span>
<span class="p">})</span>

<span class="c1">// para reemplazar web por la actual. activar Cors.</span>
<span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://wikipedia.org</span><span class="dl">'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">resp</span> <span class="o">=&gt;</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">html</span><span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nb">document</span><span class="p">.</span><span class="nx">open</span><span class="p">();</span>
	<span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">html</span><span class="p">);</span>
	<span class="nb">document</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-postput">Post/Put</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">usuario</span><span class="o">=</span> <span class="p">{</span>
    <span class="na">nombre</span><span class="p">:</span><span class="dl">'</span><span class="s1">Jose Luis</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">edad</span><span class="p">:</span> <span class="mi">36</span>
<span class="p">};</span>

<span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://reqres.in/api/users</span><span class="dl">'</span><span class="p">,{</span>
    <span class="na">method</span><span class="p">:</span><span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">usuario</span><span class="p">),</span>
    <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
    <span class="p">}</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">resp</span><span class="o">=&gt;</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">)</span>
<span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Error en la petición</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
<span class="p">});</span>

</code></pre></div></div>

<h3 id="heading-fetch-de-blobs">Fetch de Blobs</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// enlazo img con la imagen del html</span>
<span class="kd">const</span> <span class="nx">img</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">img</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">mi-imagen.png</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">resp</span><span class="o">=&gt;</span><span class="nx">resp</span><span class="p">.</span><span class="nx">blob</span><span class="p">())</span>
    <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">imagen</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">let</span> <span class="nx">imgPath</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">imagen</span><span class="p">);</span>
        <span class="nx">img</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">imagPath</span><span class="p">;</span>
    <span class="p">})</span>
</code></pre></div></div>

<h2 id="heading-http-server---servidor-web">http-server - servidor web</h2>

<p>Se necesita un servidor para arrancar la app. Pueden ser cualquiera, pero este es muy liviano.</p>

<p><a href="https://www.npmjs.com/package/http-server">www.npmjs.com/package/http-server</a></p>

<p>Una vez instalado, ejecutar <code class="language-plaintext highlighter-rouge">http-server -o</code>
Se abrirá una ventana del nevegador.</p>

<h2 id="heading-debugging">Debugging</h2>
<p>Ya que el responsive, y el móvil es tan importante en las PWA, es necesario que en todo momento trabajemos desde dispositivos móviles o similar.</p>

<p>Se puede hacer de 3 maneras, desde el mismo navegador de pc, no pudiendo probar todo.
Otra manera es desde un móvil real, y si no se tiene, virtualizando un dispositivo android.</p>

<h3 id="heading-desde-pc-en-chrome---devtools">Desde pc, en chrome - devTools</h3>
<p>En herramientas de desarrolladores (f12) - application - service worker:</p>
<ul>
  <li><strong>Offline</strong>: con ello forzamos a que la web se vea sin conexión. En una web normal se vería el dinosaurio de chrome y nos podriamos jugar unas cuantas partidas, en este caso, al ser cacheado, veremos la web de la misma manera.</li>
  <li>Update and reload: cuando se cambia algo del service worker, este solo se actualiza en usuario si se cierra la ventana y se abre otra. Se puede forzar por código y también de esta forma. Lo normal es que el usuario no lo tenga activado, actuar en consecuencia.</li>
  <li><strong>Status</strong>: un poco más abajo está status, apunta al service worker actual. Si hemos cambiado el archivo y no está activado el anterior punto, aparecerá <code class="language-plaintext highlighter-rouge">waiting to active &lt;skipWaiting&gt;</code>. Si pulsamos en esa url se actualizará el service Worker, igual que si pulsaramos <code class="language-plaintext highlighter-rouge">Update and reload</code>.</li>
  <li><strong>update / unregister</strong>: si pulsamos en unregister, borramos todo el sw, y en la próxima carga, lanzará todos los eventos desde el principio. Ojo, esto no limpia la cache, se deberá también en ese caso limpiarlo. (clear storage)</li>
</ul>

<p>En herramientas de desarrolladores (f12) - application - cache:
<strong>Cache storage</strong>: es donde se guardan nuestros archivos cacheados. De esta forma cuando la web no tenga conexión, lo cargará del cache.</p>

<p>En herramientas de desarrolladores (f12) - application - clear storage:
<strong>clear site data</strong>: es el método más eficaz para dejar localhost o la url donde estás como limpio, al volver a cargar todo empieza de nuevo, de 0.</p>

<h3 id="heading-desde-dispositivo-real">Desde dispositivo real</h3>
<ul>
  <li>
    <p>Para ello debemos de tener habilitadas las opciones de desarrollador, que por defecto están ocultas. Depende de la versión de android, y de la capa de más que tenga, por ejemplo xiaomi dispone de su propia forma de mostrarla. 
La mayoría están en ajustes - sobre el teléfono - versión (o build). Se pulsa 8 veces.</p>
  </li>
  <li>En ajustes adicionales - Opciones de desarrollador (puede tener otro nombre), habilitamos el primer check, y depuración usb.</li>
  <li>click en chrome, en devTools -  los 3 puntitos - more tools - remote devices</li>
  <li>En settings habilitamos los 2 check. añadimos nueva regla: 8080 y localhost:8080 (o el puerto que sea)</li>
  <li>click en nuestro dispositivo (previamente nuestro android nos pide que aceptemos): abrimos localhost:8080. (antes debemos abrir chrome en android).</li>
  <li>click en Inspect. En la pestaña application disponemos de todo lo necesario para borrar, ver todo sobre el service worker de nuestro móvil.</li>
</ul>

<h3 id="heading-desde-dispositivo-emulado">Desde dispositivo emulado</h3>
<ul>
  <li>Lo primero de todo es instalar android studio:
<a href="https://developer.android.com/studio">https://developer.android.com/studio</a></li>
  <li>Abrimos android studio, start a new android studio project, ponemos cualquier nombre, next, seleccionamos un api actual, superior de la 4.4, por ejemplo la 26, next, empty activity, next, finish.</li>
  <li>Click en el icono play verde (run). Creamos un nuevo virtual device, en mi caso el pixel 2 xl. Una vez descargado, ejecutamos dicho dispositivo virtual.</li>
  <li>En remote devices de devTools de chrome, si tenemos habilitado todo del anterior punto, podemos ver el dispositivo detectado, al igual que igualmente podemos inspeccionarlo, como un dispositivo más. Es muy útil para probar resoluciones como tablets o móviles pequeños.</li>
</ul>

<p>Se abre el dispositivo emulado en pantalla. 
En ese dispositivo abre chrome escribe: localhost:8080.</p>

<p>Desde el pc, podemos hacer igual que el punto anterior:
<code class="language-plaintext highlighter-rouge">chrome://inspect/#devices</code>.</p>

<h2 id="heading-lighthouse---mejora-de-calidad-web">Lighthouse - mejora de calidad web</h2>
<p>En chrome, f12, <code class="language-plaintext highlighter-rouge">Lighthouse</code> (antes llamado audit) disponemos de una herramienta para ver la calidad de cualquier web. Está separado en bloques, uno de ellos es service Worker. Si no tiene aparecerá en gris.</p>

<p>Esta herramienta es útil para poder mejorar la calidad de nuestras webs. Cualquier cambio que hagas en tu localhost puedes volver a pulsar y ver si se han solucionado los errores marcados.</p>

<p>Los bloques son:</p>

<ul>
  <li>Performance (Rendimiento)</li>
  <li>Accesibility (Accesibilidad)</li>
  <li>Best practices (Mejores prácticas)</li>
  <li>SEO</li>
  <li>Progressive Web App</li>
</ul>

<p>Puedo decir que llegar a más de un 80 según en que sección es todo un logro. Ni youtube lo consigue, que es una WPA. Simplemente, intentar conseguir el mayor número verifando los errores, los cuales son muy claros.</p>

<h2 id="heading-manifestjson">Manifest.json</h2>

<p>Archivo JSON que proporciona los metadatos necesarios para que la PWA pueda comportarse de manera más similar a una aplicación nativa: se puede instalar en la pantalla de inicio y es capaz de realizar transiciones suaves en la pantalla de inicio.</p>

<p>Configurar el json significa describir cómo se verá su PWA en la pantalla de inicio del usuario, así como cómo se verá cuando el usuario inicie su aplicación por primera vez. Además, el comportamiento de la interfaz de usuario del navegador (si estará visible u oculta).</p>

<p>Compatible con Chrome, Edge, el navegador de Android, Chrome para Android, Firefox para Android y Samsung Internet. Es parcialmente compatible con Safari.</p>

<p>Se puede crear manualmente, aunque hay webs que nos facilitan el trabajo. Un ejemplo es:
<a href="https://app-manifest.firebaseapp.com/">app-manifest.firebaseapp.com</a>
Y no solo eso, en dicha web, podemos añadir una imagen 512x512 y nos genera todas los tamaños necesarios, todo en un zip.</p>

<p>Si quieres saber más sobre el archivo manifest, consulta la siguente web:
<a href="https://developer.mozilla.org/es/docs/Web/Manifest">developer.mozilla.org/es/docs/Web/Manifest</a></p>

<p>En la raiz, creamos nuestro archivo manifest.json</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">PWA Misiones espaciales</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">short_name</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">PWA espacial</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">lang</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">es-ES</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">start_url</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/index.html</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">display</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">standalone</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">theme_color</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ff00ed</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">background_color</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#ff8200</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">icons</span><span class="dl">"</span><span class="p">:</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">images/touch/icon-128x128.png</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">sizes</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">128x128</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">images/touch/icon-192x192.png</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">sizes</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">192x192</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">images/touch/icon-256x256.png</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">sizes</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">256x256</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">images/touch/icon-384x384.png</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">sizes</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">384x384</span><span class="dl">"</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">images/touch/icon-512x512.png</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">sizes</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">512x512</span><span class="dl">"</span>
    <span class="p">}</span>
  <span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>

<p>En index.html, tag head, añadimos este código, para referenciar manifest y configurar algunos metas necesarios (algunos navegadores no soportan todas las características de manifest)</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"manifest"</span> <span class="na">href=</span><span class="s">"manifest.json"</span><span class="nt">&gt;</span>

<span class="c">&lt;!-- Todo lo demás no es obligatorio --&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"mobile-web-app-capable"</span> <span class="na">content=</span><span class="s">"yes"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-capable"</span> <span class="na">content=</span><span class="s">"yes"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"application-name"</span> <span class="na">content=</span><span class="s">"PWA Misiones espaciales"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-title"</span> <span class="na">content=</span><span class="s">"PWA Misiones espaciales"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"theme-color"</span> <span class="na">content=</span><span class="s">"#ff00ed"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"msapplication-navbutton-color"</span> <span class="na">content=</span><span class="s">"#ff00ed"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"apple-mobile-web-app-status-bar-style"</span> <span class="na">content=</span><span class="s">"black-translucent"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"msapplication-starturl"</span> <span class="na">content=</span><span class="s">"/index.html"</span><span class="nt">&gt;</span>
<span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1, shrink-to-fit=no"</span><span class="nt">&gt;</span>

<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">sizes=</span><span class="s">"128x128"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-128x128.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"128x128"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-128x128.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">sizes=</span><span class="s">"192x192"</span> <span class="na">href=</span><span class="s">"icon-192x192.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"192x192"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-192x192.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">sizes=</span><span class="s">"256x256"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-256x256.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"256x256"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-256x256.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">sizes=</span><span class="s">"384x384"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-384x384.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"384x384"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-384x384.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"icon"</span> <span class="na">sizes=</span><span class="s">"512x512"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-512x512.png"</span><span class="nt">&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"apple-touch-icon"</span> <span class="na">sizes=</span><span class="s">"512x512"</span> <span class="na">href=</span><span class="s">"/images/touch/icon-512x512.png"</span><span class="nt">&gt;</span>
</code></pre></div></div>

<p>Comprobamos si tenemos bien configurado nuestro archivo manifest.json:</p>
<ul>
  <li>chrome - devTools(f12) - application - clear storage - clear site data</li>
  <li>chrome - ctrl + f5 (actualizado)</li>
  <li>chrome - devTools - application - manifest</li>
</ul>

<p>Ya podemos ver, si está todo bien, de forma más visual nuestro archivo manifest, con sus colores, iconos, y resto de configuración.</p>

<p>Volvemos a probar Audit (chrome - devTools - audit). Podemos comprobar que tenemos nuestra PWA funcionando perfectamente.</p>

<p>Como podemos comprobar, uno de los fallos es 'Does not redirect HTTP traffic to HTTPS'. Es decir, nos indica que al ejecutarse en localhost y no en una url con https, no vamos a poder disfrutar de todo el potencial de un PWA.</p>

<h2 id="heading-notificaciones-push">Notificaciones push</h2>

<p>Permite enviar mensajes desde un servidor a un navegador (no tiene porqué estar abierto).
Se contempla en 2 fases</p>
<ul>
  <li>push: el envío desde el servidor, hacia todos los subscriptores.</li>
  <li>notificación: el mensaje en sí. la información, enforma de modal.</li>
</ul>

<h3 id="heading-comprobar-compatibilidad-navegador">Comprobar compatibilidad navegador</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="dl">'</span><span class="s1">Notification</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">window</span> <span class="o">&amp;&amp;</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Mostrar el UI para dejar que el usuario acepte poder recibir notificaciones.</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-queckear-permiso">Queckear permiso</h3>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">if</span> <span class="p">(</span><span class="nx">Notification</span><span class="p">.</span><span class="nx">permission</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">granted</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* Aquí es donde haces tu magia ;) */</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">Notification</span><span class="p">.</span><span class="nx">permission</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">blocked</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
 <span class="cm">/* El usuario ha negado previamente hacer notificaciones. No se puede hacer nada */</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="cm">/* Preguntar si da permiso el usuario */</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-solicitar-permiso">Solicitar permiso</h3>

<p>Antes de poder enviar cualquier notificación push hay que solicitar permiso. Esto no es configurable y debe ser enmascarado con un modal previo customizado para solicitarlo en un momento, desaconsejando al principio de la carga como la gran mayoría hace.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Notification</span><span class="p">.</span><span class="nx">requestPermission</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">status</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Notification permission status:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">status</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h3 id="heading-notificación-push-custom-desde-local">Notificación push custom desde local</h3>

<p>Aunque esto no es lo habitual, ya que suele ser de cara al servidor, y no el propio cliente quien genere la notificación.</p>

<p>Tiene un título, un cuerpo y un icono. Se le puede añadir vibración (si usas móvil funcionará), así como la fecha en la que se va a recibir.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">displayNotification</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">Notification</span><span class="p">.</span><span class="nx">permission</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">granted</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">navigator</span><span class="p">.</span><span class="nx">serviceWorker</span><span class="p">.</span><span class="nx">getRegistration</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">reg</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
        <span class="na">body</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Aquí el cuerpo del mensaje</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">icon</span><span class="p">:</span> <span class="dl">'</span><span class="s1">images/icons/icon-512x512.png</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">vibrate</span><span class="p">:</span> <span class="p">[</span><span class="mi">100</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">100</span><span class="p">],</span>
        <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
          <span class="na">dateOfArrival</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span>
          <span class="na">primaryKey</span><span class="p">:</span> <span class="mi">1</span>
        <span class="p">}</span>
      <span class="p">};</span>
      <span class="nx">reg</span><span class="p">.</span><span class="nx">showNotification</span><span class="p">(</span><span class="dl">'</span><span class="s1">Esto es un título</span><span class="dl">'</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
    <span class="p">});</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// hace que se muestre la notificación.</span>
<span class="nx">displayNotification</span><span class="p">()</span>

</code></pre></div></div>

<h2 id="heading-notificaciones-push-desde-angular">Notificaciones Push desde Angular</h2>
<p>Para ello hay que seguir unos pasos muy bien explicados en esta web.
<a href="https://blog.angular-university.io/angular-push-notifications/">angular-university.io/angular-push-notifications</a></p>

<h2 id="heading-transformación-de-web-a-pwa">Transformación de web a PWA</h2>

<h3 id="heading-manualmente">Manualmente</h3>

<p>Desaconsejado, son muchos pasos que de otra manera te los puedes saltar.</p>
<ul>
  <li>Añadir un archivo js. En él se deben de crear todos los eventos de serviceWorker, pasando por todas las etapas, definiendo los tipos de cacheados. Es muy fácil equivocarse y el tiempo se hace largo.</li>
  <li>Añadir <code class="language-plaintext highlighter-rouge">manifest.json</code>.</li>
</ul>

<h3 id="heading-workbox">Workbox</h3>

<p><code class="language-plaintext highlighter-rouge">Workbox</code> es una colección de distintas librerías y herramientas creadas por Google y que nos ayudan en la creación y simplificación de service workers para nuestras Progressive Web Apps.</p>

<p>Ver post 2/2 de PWA.</p>

<h3 id="heading-angular-e-ionic">Angular e Ionic</h3>

<p>Para más información en la web de ionic <a href="https://ionicframework.com/docs/angular/pwa">ionicframework.com/docs/angular/pwa</a></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ng</span> <span class="nx">add</span> <span class="p">@</span><span class="nd">angular</span><span class="sr">/pw</span><span class="err">a
</span></code></pre></div></div>

<!-- https://pwaexperts.io/tutoriales -->]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="PWA" /><category term="javascript" /><summary type="html"><![CDATA[Tutorial, consejos y primeros pasos para el desarrollo de PWA.]]></summary></entry><entry><title type="html">PWA - Aplicaciones Web Progresivas 2/2 - Workbox</title><link href="https://jolugama.com/blog/2021/05/27/PWA-workbox/" rel="alternate" type="text/html" title="PWA - Aplicaciones Web Progresivas 2/2 - Workbox" /><published>2021-05-27T07:00:00+00:00</published><updated>2021-05-27T07:00:00+00:00</updated><id>https://jolugama.com/blog/2021/05/27/PWA-workbox</id><content type="html" xml:base="https://jolugama.com/blog/2021/05/27/PWA-workbox/"><![CDATA[<blockquote>
  <p>Tutorial de uso de <strong>Workbox</strong> para agilizar la creación de PWA, aplicaciones web progresivas.</p>
</blockquote>

<div id="toc"></div>

<h2 id="heading-qué-es">Qué es?</h2>
<p>Es un set de <code class="language-plaintext highlighter-rouge">bibliotecas</code> creadas cómo no, por <strong>Google</strong>, que ayudan a la creación de service workers para las PWA. Dispone de varias estrategias de cacheado ya predefinidas para poder manejar html, css,js, imágenes, etc. Es importar y usar, de una forma  fácil.</p>

<p>Realmente se hace obligatorio su uso, ya que hacerlo todo manual como ya expuse en el anterior post, se hace un tanto difícil con un alto índice de fallos que hace perder mucho más tiempo XD.</p>

<h2 id="heading-demo-y-código-fuente">Demo y código fuente</h2>

<p>Para hacerlo más amigable he creado un proyecto cool en github para que no sea una experiencia perturbadora.</p>

<ul>
  <li>Código fuente lo puedes descargar en <a href="https://github.com/jolugama/notification-push-firebase">github.com/jolugama/notification-push-firebase</a>. Dale like si te gusta, es bonito ver que tu trabajo servío para algo.</li>
  <li>Demo en <a href="https://jolugama.github.io/notification-push-firebase/public/">jolugama.github.io/notification-push-firebase/public</a>.</li>
</ul>

<p>Si deambulas en los escabrosos archivos, puedes ver en <code class="language-plaintext highlighter-rouge">firebase-messaging-sw.js</code> que está todo workbox en él, en muy pocas lineas. Usa jquery, sin frameworks.</p>

<p>Dispone de firebase para notificaciones push, pero no vas a poderlo ver, ya que el único que puede mandar esas notificaciones soy yo, y no voy a envíar notificaciones a diario para chequearlo. Así amigo, te animo a que con unos pequeños pasos lo hagas tú mismo, lo explicaré, o quizás ya lo veas explicado, en futuros o presentes posts. Buscar por firebase.</p>

<p>Este ejemplo usa la <strong>estrategia más habitual</strong>, que es servir la web con todo su contenido, cachear hasta un máximo prefijado (se puede cambiar) y si falta internet lo muestra como si estuvieras con ella.</p>

<p>Para que se active el service worker, debes pasar al 100% del scroll para que salte. Acepta. Para eliminarlo luego pulsa en el candado de  la izquierda en la barra de direcciones.</p>

<h2 id="heading-importante-antes-de-empezar">Importante, antes de empezar</h2>
<p>La guía oficial <a href="https://developers.google.com/web/tools/workbox/guides/get-started">developers.google.com/web/tools/workbox/guides/get-started</a> nos explica como crearlas de una manera fácil y cómoda, salvo que en mi opinión, a fecha de hoy (está en continuo cambio), no está en órden y faltan algunos datos, por eso este post.</p>

<p>Todo ejemplo en dicha web está preparada para usarlo con módulos. Qué pasa con los <code class="language-plaintext highlighter-rouge">modules</code> en javascript? Que javascript vanilla no puede usarlo y si tu proyecto está en jquery o similar, deberás cargarlo de otra forma (mediante CDN, y con this.workbox, como indico más adelante.)</p>

<h2 id="heading-importación-instalación-uso">Importación, instalación, uso</h2>

<p>Hay 2 formas de importarlo.</p>

<h3 id="heading-usando-cdn-la-alternativa-a-los-módulos">Usando CDN, la alternativa a los módulos</h3>

<p>La pongo primera ya que el tutorial oficial lo deja un poco de lado, pero su uso es menor, ya que está pensada para proyectos pequeños que no usen webpack, ni frameworks como angular.</p>

<ul>
  <li>Crea un archivo sw.js, impórtalo en el html. <strong>Debe estar importado en todos los html</strong>.</li>
  <li>importa por cdn workbox
    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">importScripts</span><span class="p">(</span><span class="dl">'</span><span class="s1">https://storage.googleapis.com/workbox-cdn/releases/6.1.5/workbox-sw.js</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Todos los ejemplos de la web usan los métodos sin el nombre de la clase, ni el namespace, por lo que hay que incluirlo.</p>

    <p>ejemplo:</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">registerRoute</span><span class="p">(</span>
</code></pre></div>    </div>

    <p>debe ser reemplazado por: (es decir, incluir clase y namespace)</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">workbox</span><span class="p">.</span><span class="nx">routing</span><span class="p">.</span><span class="nx">registerRoute</span><span class="p">(</span>
</code></pre></div>    </div>
  </li>
  <li>También puedes importarlo localmente, e igualmente importarlo con <code class="language-plaintext highlighter-rouge">importScripts</code>.</li>
</ul>

<h3 id="heading-método-por-default-con-importación-de-módulos-uso-plugins">Método por default con importación de módulos, uso plugins</h3>

<ul>
  <li>Seguir los ejemplos tal cual de la web anterior descrita.</li>
  <li>
    <p>Todo va por módulos, si algo quieres, antes debes instalarlo mediante npm. 
Aquí tienes todas los módulos actuales para instalar: <a href="https://developers.google.com/web/tools/workbox/modules">developers.google.com/web/tools/workbox/modules</a>.
Tan fácil como hacer <code class="language-plaintext highlighter-rouge">npm install workbox-core</code></p>
  </li>
  <li>
    <p>Si quieres instalarte los módulos más importantes, y guardarlos en package.json</p>

    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">npm install --save workbox-core workbox-background-sync workbox-expiration workbox-google-analytics workbox-navigation-preload workbox-precaching workbox-routing workbox-strategies
</span></code></pre></div>    </div>

    <p>Con estos módulos, creo que no me falta ninguno importante, puedes hacer casi cualquier cosa. Si te falta alguno, instala de nuevo con <code class="language-plaintext highlighter-rouge">--save</code>.</p>
  </li>
  <li>Un ejemplo casi completo que puedes mejorar es el que te viene en la primera sección <a href="https://developers.google.com/web/tools/workbox/guides/get-started">Get Started</a>.</li>
</ul>

<h2 id="heading-manifestjson">Manifest.json</h2>

<p>Todo service worker necesita su propio archivo manifest.json en raíz. Debes de crearlo. Esto ya lo explico detalladamente en el anterior post.</p>

<h2 id="heading-página-offline-e-imágen-por-defecto">Página offline e imágen por defecto</h2>

<p>Cuando se va internet en casa o falta de cobertura en móvil, nos interesa que si hay una imágen que no tenemos, nos muestre una por defecto ya cacheada y si el link que queremos acceder no lo tenemos cacheado, que nos responda con una página por defecto. Difícil? Con <strong>workbox</strong> no!.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">workbox</span><span class="p">.</span><span class="nx">precaching</span><span class="p">.</span><span class="nx">precacheAndRoute</span><span class="p">([{</span>
        <span class="na">url</span><span class="p">:</span> <span class="dl">'</span><span class="s1">offline.html</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">revision</span><span class="p">:</span> <span class="kc">null</span>
    <span class="p">},</span>
    <span class="p">{</span>
        <span class="na">url</span><span class="p">:</span> <span class="dl">'</span><span class="s1">images2/no-image.png</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">revision</span><span class="p">:</span> <span class="kc">null</span>
    <span class="p">},</span>
    <span class="c1">// ... other entries ...</span>
<span class="p">]);</span>

<span class="c1">// Catch routing errors, like if the user is offline</span>
<span class="k">this</span><span class="p">.</span><span class="nx">workbox</span><span class="p">.</span><span class="nx">routing</span><span class="p">.</span><span class="nx">setCatchHandler</span><span class="p">(</span><span class="k">async</span> <span class="p">({</span>
    <span class="nx">event</span>
<span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">event</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">event.request.destination</span><span class="dl">'</span><span class="p">,</span> <span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">destination</span><span class="p">);</span>
    <span class="c1">// Return the precached offline page if a document is being requested</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">destination</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">document</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">workbox</span><span class="p">.</span><span class="nx">precaching</span><span class="p">.</span><span class="nx">matchPrecache</span><span class="p">(</span><span class="dl">'</span><span class="s1">offline.html</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">destination</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">image</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">workbox</span><span class="p">.</span><span class="nx">precaching</span><span class="p">.</span><span class="nx">matchPrecache</span><span class="p">(</span><span class="dl">'</span><span class="s1">images2/no-image.png</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nx">Response</span><span class="p">.</span><span class="nx">error</span><span class="p">();</span>
<span class="p">});</span>


</code></pre></div></div>

<p>Si usas módulos, quita this.workbox.precaching y this.workbox.routing</p>

<h2 id="heading-solución-de-problemas-y-depuración-debug-desde-chrome">Solución de problemas y depuración (debug) desde Chrome</h2>

<p>En herramientas de desarrollo de Chrome, tab Application.</p>

<ul>
  <li>Verifica tab <code class="language-plaintext highlighter-rouge">Manifest</code>, si todo está correcto, pruébalo en móvil. Borra cookies si te falla, instala app.</li>
  <li>Una manera de levantar un proyecto público y fácil, es desde <code class="language-plaintext highlighter-rouge">github pages</code>, o <code class="language-plaintext highlighter-rouge">firebase hosting</code>.</li>
  <li>Usa el <strong>panel de service worker</strong> para cancelar registro, actualizar servicio, emular eventos push,  detener service worker.</li>
  <li>Verifica, borra y manipula el <strong>cache</strong> del service worker.</li>
  <li>Borra service worker, todo el almacenamiento y cachés en un sólo click desde el panel storage.</li>
</ul>

<p>Todo esto y mucho más en <a href="https://developer.chrome.com/docs/devtools/progressive-web-apps/">developer.chrome.com/docs/devtools/progressive-web-apps</a>.</p>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="PWA" /><category term="javascript" /><summary type="html"><![CDATA[Tutorial de uso de Workbox para agilizar la creación de PWA, aplicaciones web progresivas.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jolugama.com/workbox-pwa.png" /><media:content medium="image" url="https://jolugama.com/workbox-pwa.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">MongoDB - tutorial básico - Atlas, Compass</title><link href="https://jolugama.com/blog/2021/05/24/mongodb-tutorial-basico/" rel="alternate" type="text/html" title="MongoDB - tutorial básico - Atlas, Compass" /><published>2021-05-24T07:00:00+00:00</published><updated>2021-05-24T07:00:00+00:00</updated><id>https://jolugama.com/blog/2021/05/24/mongodb-tutorial-basico</id><content type="html" xml:base="https://jolugama.com/blog/2021/05/24/mongodb-tutorial-basico/"><![CDATA[<!-- # MongoDB - primeros pasos -->
<blockquote>
  <p>Tutorial, consejos y guía de aprendizaje básico para MongoDB</p>
</blockquote>

<div id="toc"></div>

<h2 id="heading-qué-es-mongodb">Qué es mongoDB</h2>

<p>Es un sistema de base de datos NoSQL, orientado a documentos y de código abierto.
En lugar de guardar los datos en tablas, los guarda en BSON, similar a JSON, haciendo que el acceso sea mucho más rápido.
Disponible para Linux, OS X, Windows. Ver más información: <a href="https://es.wikipedia.org/wiki/MongoDB">mongoDB en wikipedia</a>.
Entre los NoSQL están: MongoDB, Cassandra, HBase, Neo4j.</p>

<h2 id="heading-jerarquía">Jerarquía</h2>

<p>Un cluster puedes tener de 1 a 3 cluster (de forma gratuita, si no más), cada uno de ellos puede tener x databases, cada uno de ellos varios collectión y a su vez documents.</p>

<p><code class="language-plaintext highlighter-rouge">CLUSTER - DBS - COLLECTIONS - DOCUMENTS</code> .</p>

<h2 id="heading-sql-vs-nosql">SQL vs NOSQL</h2>

<p>Una comparativa de terminología usada en SQL frente a NOSQL (con MongoDB y Cassandra)</p>

<table>
  <thead>
    <tr>
      <th>SQL</th>
      <th>MongoDB</th>
      <th>Cassandra</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Table</td>
      <td>Collection</td>
      <td>Table</td>
    </tr>
    <tr>
      <td>Row</td>
      <td>Document</td>
      <td>Row</td>
    </tr>
    <tr>
      <td>Column</td>
      <td>field</td>
      <td>Column</td>
    </tr>
    <tr>
      <td>Primary key</td>
      <td>ObjectId</td>
      <td>Primary key</td>
    </tr>
    <tr>
      <td>Index</td>
      <td>Index</td>
      <td>Index</td>
    </tr>
    <tr>
      <td>View</td>
      <td>View</td>
      <td>Materialized view</td>
    </tr>
    <tr>
      <td>Nested table or object</td>
      <td>Embedded document</td>
      <td>Map</td>
    </tr>
    <tr>
      <td>Array</td>
      <td>Array</td>
      <td>List</td>
    </tr>
  </tbody>
</table>

<h2 id="heading-donde-aprender-mongodb">Donde aprender MongoDB</h2>

<p>En la web oficial <a href="https://university.mongodb.com/">university.mongodb.com</a> hay un montón de cursos 100% gratuitos, en inglés subtitulados en castellano. Se entienden muy bien, se hace rápido y te dan titulación oficial.
Cada curso tiene un nivel, y hay cursos que son dependientes de otros más básicos.</p>

<p>De todos estos destaco los que más me gustan:</p>
<ul>
  <li>MongoDB Basics</li>
  <li>MongoDB for Javascript Developers</li>
  <li>MongoDB for Python Developers</li>
  <li>MongoDB for SQL Pros</li>
  <li>Introduction to MongoDB Charts</li>
</ul>

<p>No te preocupes por su instalación, no necesita, usa atlas, en la nube.</p>

<h2 id="heading-hacia-la-nube-y-más-allá---cloud-con-mongodb-atlas">Hacia la nube y más allá - Cloud con MongoDB Atlas</h2>

<p>Es un servicio de Base de Datos en la nube, que te permite crear, administrar la BBDD. Esto evita tener que instalar nada, mantener un servidor.</p>

<p>Como todo servicio tiene su coste, pero hay una modalidad gratuita, que es para equipos de aprendizaje y desarrolladores de pequeñas aplicaciones. Dispones de 512mb de almacenamiento, de sobra para cualquier mini proyecto que estés empezando y puedes usar hasta 3 server replica set y lo mejor de todo, sin límite de caducidad, nunca se borrará.</p>

<p>Para acceder al entorno, entrar en <a href="https://www.mongodb.com/cloud/atlas">mongodb.com/cloud/atlas</a>. Puedes acceder con la cuenta de google.</p>

<p>Para trabajar antes deberás crear:</p>
<ul>
  <li>1 organización</li>
  <li>1 proyecto</li>
  <li>1 cluster</li>
</ul>

<p>Importante cuando te pregunta de la lista (whitelist) de direcciones ip,  poner <code class="language-plaintext highlighter-rouge">Allow Access from Anywhere</code>.</p>

<p>Añadir usuario y contraseña.</p>

<p>En tu cluster puedes hacer 3 cosas básicamente (pulsando a conectar):</p>

<ul>
  <li>Interactuar con <code class="language-plaintext highlighter-rouge">shell</code>: usando una interface de mongodb en javascript. Puedes crear una máquina virtual (de pago), o usar tu propio shell (debes instalarlo en tu pc)</li>
  <li>Conectar tu aplicación con tu cluster mediante drivers nativos</li>
  <li>Usar <code class="language-plaintext highlighter-rouge">MongoDB Compass</code>: explora, modifica y visualiza tu BBDD mediante esta GUI.</li>
</ul>

<h2 id="heading-instalación-shell">Instalación shell</h2>

<p>En la instalación de MongoDB server ya viene incluido el shell. Alternativamente puedes descargar separadamente solo el shell desde la web de documentación <a href="https://docs.mongodb.com/manual/mongo/">docs.mongodb.com</a> seguir las instrucciones. Básicamente es:</p>
<ul>
  <li>Descargar el sistema operativo</li>
  <li>Elegir el paquete, el shell o server. Recomiendo server ya que es el completo para hacer tus pruebas en tu pc.</li>
  <li>para ubuntu, mejor esta guía (sino, no se instala shell): <a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-enterprise-on-ubuntu/">mongodb-interprise-on-ubuntu</a></li>
  <li>para windows: <a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-enterprise-on-windows/">mongodb-interprise-on-windows</a></li>
  <li>Una vez instalado lanza servicio mongo:
    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">sudo systemctl start mongod
</span></code></pre></div>    </div>
  </li>
  <li>Ver el estado servicio
    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">sudo systemctl status mongod
</span></code></pre></div>    </div>
  </li>
  <li>Inicio automático en cada arranque
    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">sudo systemctl enable mongod
</span></code></pre></div>    </div>
  </li>
</ul>

<h2 id="heading-conectarnos-a-shell">Conectarnos a shell</h2>

<p>En cluster de atlas, pulsar connect, pulsar que tienes un shell instalado. Copiar la linea:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mongo "mongodb+srv://USUARIO:CONTRASENIA@CLUSTER/COLLECTION"
</span></code></pre></div></div>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mongo "mongodb+srv://m001-student:m001-mongodb-basics@sandbox.wzwpg.mongodb.net/myFirstDatabase"

</span></code></pre></div></div>

<h2 id="heading-mongodb-compass">MongoDB Compass</h2>
<p>No es la única, si la oficial. otra opción es <code class="language-plaintext highlighter-rouge">Robo 3T</code>. <a href="https://robomongo.org/download">robomongo.org</a></p>

<p>Compass es una herramienta muy completa para usar el lenguaje MQL de una manera muy cómoda. Visualización de databases, colecciones, documentos, todo muy visual, muy vistoso y con muchas opciones. Tanto si estás aprendiendo, como si ya controlas, esta herramienta es en mi opinión, obligatoria. Para todos los sistemas operativos. 
<a href="https://www.mongodb.com/try/download/compass">mongodb.com/download/compass</a></p>

<h2 id="heading-visual-studio-code---mongodb-ide">Visual studio code - MongoDB IDE</h2>

<p>Trabajar en terminal puede resultar un poco duro, Atlas UI quizás no es lo que buscas.</p>

<p>Una de las formás que más me gusta es desde visual studio code y conectarme al cluster de Atlas. Para ello debes de instalar el plugin <code class="language-plaintext highlighter-rouge">MongoDB for VS Code</code>. Se habilitará un icono en forma de hoja vertical a la izquierda.</p>

<p>Es ahí donde añades la conexión, que actualmente y esto puede cambiar a mejor siempre, tienes 2 sistemas, por la uri (todo en una misma url) o del método tradicional con host, usuario, contraseña…. personalmente prefiero la primera.</p>

<p>Puedes conectarte remotamente a atlas, o a un mongo que tengas local.</p>

<p>Una vez conectado, puedes hacer scripts, lo bueno es que lo colorea y visualmente se ve muy bien. Para ello ctrl+shift+p  <code class="language-plaintext highlighter-rouge">MongoDB: Create MongoDB Playground</code>. Puedes usar doble barra para comentarios.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//select the database tu use</span>
<span class="nx">show</span> <span class="nx">dbs</span>
<span class="nx">use</span><span class="p">(</span><span class="dl">'</span><span class="s1">sample_training</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">show</span> <span class="nx">collections</span>
<span class="nx">db</span><span class="p">.</span><span class="nx">inspections</span><span class="p">.</span><span class="nx">insert</span><span class="p">({</span><span class="dl">"</span><span class="s2">id</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">12345-ttt</span><span class="dl">"</span><span class="p">,</span><span class="na">certificate_number</span><span class="p">:</span><span class="mi">1234</span><span class="p">,</span><span class="dl">"</span><span class="s2">business_name</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">abcde</span><span class="dl">"</span><span class="p">})</span>
<span class="nx">db</span><span class="p">.</span><span class="nx">inspections</span><span class="p">.</span><span class="nx">find</span><span class="p">({}).</span><span class="nx">sort</span><span class="p">({</span><span class="na">_id</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span><span class="p">}).</span><span class="nx">limit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> 

</code></pre></div></div>

<p>Arriba a la derecha hay un icono de play. Al pinchar se muestra una ventana colorida del resultado (json).</p>

<p>Otro punto es que tiene autocompletado de colecciones y demás. Para más información hay varios videos en youtube: How to Use Visual Studio Code as Your MongoDB IDE.</p>

<h2 id="heading-json-vs-bson">JSON Vs BSON</h2>

<table>
  <thead>
    <tr>
      <th>JSON</th>
      <th>BSON</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>codificado en utf8 string</td>
      <td>codificado en binario</td>
    </tr>
    <tr>
      <td>tipos: String, Boolean, Number,Array</td>
      <td>tipos: String, Boolean, Number (Integer, Long, Float,…), Array, Date, Raw Binary</td>
    </tr>
    <tr>
      <td>legible por humanos y máquinas</td>
      <td>sólo máquinas.</td>
    </tr>
    <tr>
      <td>mongoimport</td>
      <td>mongorestore</td>
    </tr>
    <tr>
      <td>mongoexport</td>
      <td>mongodump</td>
    </tr>
  </tbody>
</table>

<p>Es decir, guardamos en BSON (con un soporte de tipos más enriquecido), pero lo leemos como un JSON.</p>

<h2 id="heading-cargar-ejemplo-dataset">Cargar ejemplo Dataset</h2>

<p>Una vez creado un cluster, y db, en el botón de los 3 puntitos, seleccionar <code class="language-plaintext highlighter-rouge">Load Sample Dataset</code>. Se descargará varias databases con sus respectivos collections. Ocupa 350mb, como el límite gratuito son de 500mb solo podrás tener 1 ejemplo importado.</p>

<h2 id="heading-copias-de-seguridad-y-restauración">Copias de seguridad y restauración</h2>

<p>Para conocer más a fondo todas las opciones que tiene cada uno, en consola, usar <code class="language-plaintext highlighter-rouge">--help</code>. En consola escribe mongo y tabulador, verás que hay otros pocos comandos más, como monodecript, mongofiles, mongokerberos</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mongoexport --help
</span></code></pre></div></div>

<ul>
  <li>mongoimport: importa el contenido de archivos JSON, CSV, TSV. Son colecciones enteras.</li>
</ul>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">mongoimport --uri "&lt;Atlas Cluster URI&gt;</span><span class="s2">" --drop miColeccion.json --collection miColeccion
</span></code></pre></div></div>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mongoimport --uri  "mongodb+srv://m001-student:m001-mongodb-basics@sandbox.wzwpg.mongodb.net/sample_supplies" --drop sales.json --collection sales
</span></code></pre></div></div>

<p>Si no se especifica la colección, la tomará del propio archivo json. <code class="language-plaintext highlighter-rouge">--drop</code> hace borrar la anterior colección e incorporar la nueva. Si no lo ponemos e insertamos, si tienen la misma id, dará error de inserción en cada documento.</p>

<ul>
  <li>mongoexport: exporta una colección de una db a un archivo JSON</li>
</ul>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">mongoexport --uri "&lt;Atlas Cluster URI&gt;</span><span class="s2">" --collection miColeccion  --out nombreArchivo.json 
</span></code></pre></div></div>

<p>Guarda en nombreArchivo.json la colección miColeccion del cluster del uri.</p>

<ul>
  <li>mongorestore: restaura una copia de seguridad binaria (BSON) de una base de datos, creada con el comando <code class="language-plaintext highlighter-rouge">mongodump</code>.</li>
</ul>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">mongorestore --uri "&lt;Atlas Cluster URI&gt;</span><span class="s2">" --drop directorio_Donde_se_aloja_BSON
</span></code></pre></div></div>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mongorestore --uri  "mongodb+srv://m001-student:m001-mongodb-basics@sandbox.wzwpg.mongodb.net/sample_supplies" dump/sample_supplies
</span></code></pre></div></div>

<p>Si no se ha tocado nada, y no hay diferencias, dará un error. Puedes probar a borrar la bd para recuperarla de nuevo.</p>

<p>Donde dump es la carpeta donde se guardó con mongodump. En este caso recuperamos toda la db, que solo tiene una colección pero pudiera tener muchas más.</p>

<ul>
  <li>mongodump: realiza un backup de una database (con todas sus colecciones). Lo exporta en BSON.</li>
</ul>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">mongodump --uri "&lt;Atlas Cluster URI&gt;</span><span class="s2">"
</span></code></pre></div></div>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go"> mongodump --uri  "mongodb+srv://m001-student:m001-mongodb-basics@sandbox.wzwpg.mongodb.net/sample_supplies"
</span></code></pre></div></div>

<p>En mi ejemplo, guarda dentro de la carpeta <code class="language-plaintext highlighter-rouge">dump</code> la database(db) sample_supplies la cual contiene una sola colección "sales".</p>

<h2 id="heading-consultar-collections-y-databases">Consultar collections y databases</h2>

<ul>
  <li>
    <p>Desde Atlas, mediante data explorer
  Clickando en collections.</p>

    <p>Un namespace es una concatenación de database + collection.</p>
  </li>
  <li>
    <p>Desde shell desde tu pc o máquina virtual.</p>

    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">  mongo "mongodb+srv://m001-student:m001-mongodb-basics@sandbox.wzwpg.mongodb.net/admin"

</span><span class="gp">  mongo "mongodb+srv://&lt;username&gt;</span>:&lt;password&gt;@&lt;cluster&gt;.mongodb.net/admin<span class="s2">"    
</span></code></pre></div>    </div>

    <p>Una vez accedido, se puede usar los siguientes comandos:</p>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">show dbs</code>:  muestra todas las database</li>
      <li><code class="language-plaintext highlighter-rouge">use</code> databaseExample: acceder a la database</li>
      <li><code class="language-plaintext highlighter-rouge">show collections</code>: muestra todas sus colecciones.</li>
      <li>hacer una búsqueda con filtros:
        <ul>
          <li>ej: db.nombreCollection.find( {"state":"NY"} )</li>
          <li>ej: db.nombreCollection.find( {"state":"NY"} ).count()</li>
          <li>ej: db.nombreCollection.find( {"state":"NY"} ).pretty()</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="heading-crear-y-manipular-documentos">Crear y manipular documentos</h2>

<ul>
  <li>
    <p>Desde Atlas UI
En clusters, selecciona una db, click en el collection a insertar, botón <code class="language-plaintext highlighter-rouge">insert document</code>. Pulsando + o tab se pasa al siguiente.</p>
  </li>
  <li>
    <p>Desde shell</p>
    <div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">  show dbs
  use miDataBase
  show collections
  db.inspections.insert({"id":"12345-ttt",certificate_number:1234,"business_name":"abcde"})
  db.inspections.find({}).sort({_id:-1}).limit(1) 
</span></code></pre></div>    </div>
  </li>
</ul>

<p>No hace falta añadir el atributo <code class="language-plaintext highlighter-rouge">_id</code>, ya que así se genera automáticamente.</p>

<p>A tener en cuenta: <code class="language-plaintext highlighter-rouge">_id</code> es el identificador único de un documento en una colección. Es requerido en cada documento de MongoDB. Si usamos <code class="language-plaintext highlighter-rouge">ObjectId()</code>, MongoDB creará una id única, por lo que es lo más recomendable, y no crear nosotros mismos una id ya sea string o number.</p>

<h2 id="heading-crud---create-read-update-delete">CRUD - CREATE, READ, UPDATE, DELETE</h2>

<p>Operaciones básicas para documentos de colecciones. Crear, leer, actualizar y borrar documentos de colecciones de databases.</p>

<ul>
  <li><a href="https://docs.mongodb.com/manual/reference/">Documentación mongoDB</a>. Se trata de una guía muy completa, con un buscador arriba rápido y eficiente. A la izquierda tienes que seleccionar cual es la vs que usas.</li>
</ul>

<p>Las operaciones más básicas las puedes  ver en <a href="https://docs.mongodb.com/manual/crud/">docs.mongodb.com/manual/crud/</a></p>

<ul>
  <li>Read. Realizar consultas, queries. puedes usar <code class="language-plaintext highlighter-rouge">find</code> (básico) o <code class="language-plaintext highlighter-rouge">aggregate</code> (es un completo framework que funciona a modo tuberías - pipes)</li>
  <li>Update. Para actualizar documentos están <code class="language-plaintext highlighter-rouge">updateOne</code>, <code class="language-plaintext highlighter-rouge">updateMany</code> y <code class="language-plaintext highlighter-rouge">replaceOne</code>.</li>
  <li>Delete. Para borrar documentos se usa <code class="language-plaintext highlighter-rouge">deleteOne</code> y <code class="language-plaintext highlighter-rouge">deleteMany</code>.</li>
  <li>Create. Para crear con <code class="language-plaintext highlighter-rouge">insertOne</code> y <code class="language-plaintext highlighter-rouge">insertMany</code>.</li>
</ul>

<h2 id="heading-framework-aggregation">Framework Aggregation</h2>

<p>Una forma más pro de realizar consultas, en vez de find, se usa aggregation.</p>

<p>Más información en <a href="https://docs.mongodb.com/manual/aggregation/">docs.mongodb.com/manual/aggregation</a>.</p>

<p>Lo que hace es por bloques, de una forma más organizada, se ve mucho mejor, más fácil de mantener. Personalmente lo veo un código mucho más limpio.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">db</span><span class="p">.</span><span class="nx">orders</span><span class="p">.</span><span class="nx">aggregate</span><span class="p">([</span>
   <span class="p">{</span> <span class="na">$match</span><span class="p">:</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="dl">"</span><span class="s2">A</span><span class="dl">"</span> <span class="p">}</span> <span class="p">},</span>
   <span class="p">{</span> <span class="na">$group</span><span class="p">:</span> <span class="p">{</span> <span class="na">_id</span><span class="p">:</span> <span class="dl">"</span><span class="s2">$cust_id</span><span class="dl">"</span><span class="p">,</span> <span class="na">total</span><span class="p">:</span> <span class="p">{</span> <span class="na">$sum</span><span class="p">:</span> <span class="dl">"</span><span class="s2">$amount</span><span class="dl">"</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
<span class="p">])</span>

</code></pre></div></div>

<p>Una forma de probarlo muy visual es con <code class="language-plaintext highlighter-rouge">Compass</code>, con solo hacer clicks, dificil equivocarse, con un entorno muy agradable.</p>

<h2 id="heading-nota">Nota</h2>

<p>Se que explico mucho y nada. Me gustaría que te hayas quedado con estas nociones generales, es una visión general de MongoDB, que sin duda, si la sigues puedes aprender mucho. Espero que te haya servido. Ten mucho en cuenta las url, que pueden haber cambiado en su tiempo, pero la idea es lo que importa. La api de Mongo es muy útil, y a diferencia de otras, esta tiene las 3 b: buena, bonita y barata.</p>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="MongoDB" /><summary type="html"><![CDATA[Tutorial, consejos y guía de aprendizaje básico para NOSQL MongoDB.]]></summary></entry><entry><title type="html">Angular 9 - Componentes dinámicos, sin entryComponents</title><link href="https://jolugama.com/blog/2020/02/25/angular9-componentes-dinamicos/" rel="alternate" type="text/html" title="Angular 9 - Componentes dinámicos, sin entryComponents" /><published>2020-02-25T09:00:00+00:00</published><updated>2020-02-25T09:00:00+00:00</updated><id>https://jolugama.com/blog/2020/02/25/angular9-componentes-dinamicos</id><content type="html" xml:base="https://jolugama.com/blog/2020/02/25/angular9-componentes-dinamicos/"><![CDATA[<blockquote>
  <p>Guia para hacer componentes dinámicos en Angular 9, una manera más rápida y limpia, con menos lineas y sin tocar los módulos.</p>
</blockquote>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-1-elegir-dónde-cargar-el-componente-dinámico" id="markdown-toc-heading-1-elegir-dónde-cargar-el-componente-dinámico">1. Elegir dónde cargar el componente dinámico</a></li>
  <li><a href="#heading-2-fabricador-de-componentes-dinámicos-componentfactoryresolver" id="markdown-toc-heading-2-fabricador-de-componentes-dinámicos-componentfactoryresolver">2. Fabricador de componentes dinámicos: ComponentFactoryResolver</a></li>
  <li><a href="#heading-3-crear-factoría-y-visualizar-componente" id="markdown-toc-heading-3-crear-factoría-y-visualizar-componente">3. Crear factoría y visualizar componente</a></li>
  <li><a href="#heading-4-acceso-a-los-métodos-y-propiedades-públicos" id="markdown-toc-heading-4-acceso-a-los-métodos-y-propiedades-públicos">4. Acceso a los métodos y propiedades públicos</a></li>
  <li><a href="#heading-5-destruir-componente-dinámico" id="markdown-toc-heading-5-destruir-componente-dinámico">5. Destruir componente dinámico</a></li>
</ul>

<p>Ha cambiado ligeramente la manera de incorporar componentes dinámicos a nuestro Angular con la nueva llegada de la versión 9. Nos olvidamos de añadir al módulo mediante <code class="language-plaintext highlighter-rouge">entryComponents</code> los componentes que se van a incorporar dinámicamente después. Ahora todo se carga en caliente desde el componente mediante <code class="language-plaintext highlighter-rouge">async await</code>.</p>

<h2 id="heading-1-elegir-dónde-cargar-el-componente-dinámico">1. Elegir dónde cargar el componente dinámico</h2>

<p>No ha cambiado nada. Justo donde se quiere añadir dicho componente dinámico, añadir una <code class="language-plaintext highlighter-rouge">template reference variable</code> para tener una referencia al elemento.</p>

<p>En el html:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
<span class="nt">&lt;ng-container</span> <span class="na">#componenteDinamico</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
</code></pre></div></div>

<p>Podría ser un div, pero se crearía un padre div estuviera creado o no dicho componente dinámico. Para eliminar ese div innecesario.</p>

<p>Todo lo demás se hace en el ts.</p>
<ul>
  <li>Importa los componentes a referenciar.</li>
  <li>Crea una referencia a este elemento con <code class="language-plaintext highlighter-rouge">ViewChild</code> de tipo <code class="language-plaintext highlighter-rouge">ViewContainerRef</code>.</li>
</ul>

<p>En el ts:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">ItemsListComponent</span> <span class="kr">implements</span> <span class="nx">OnInit</span><span class="p">,</span> <span class="nx">OnDestroy</span><span class="p">,</span> <span class="nx">AfterViewInit</span> <span class="p">{</span>
  <span class="nl">miFactory</span><span class="p">:</span> <span class="nx">ComponentFactory</span><span class="o">&lt;</span><span class="nx">any</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nl">componentRef</span><span class="p">:</span> <span class="nx">ComponentRef</span><span class="o">&lt;</span><span class="nx">miDinamicoComponent</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// se declara una variable referencia.</span>
  <span class="nl">componentRef2</span><span class="p">:</span> <span class="nx">ComponentRef</span><span class="o">&lt;</span><span class="nx">miDinamicoComponent2</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// se declara una variable referencia.</span>
  <span class="c1">// ViewContainerRef crea componentes en su interior de forma dinámica</span>
  <span class="c1">// Hay que añadir read: ViewContainerRef, ya que si no, devolvería un ElementRef,</span>
  <span class="c1">// que es lo que devuelve por defecto un viewChild.</span>
  <span class="c1">// la referencia que tenemos es compDynamicContainer, y en ese contenedor añadiremos los componentes dinámicos</span>
  <span class="p">@</span><span class="nd">ViewChild</span><span class="p">(</span><span class="dl">'</span><span class="s1">componenteDinamico</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">read</span><span class="p">:</span> <span class="nx">ViewContainerRef</span> <span class="p">})</span> <span class="nx">compDynamicContainer</span><span class="p">:</span> <span class="nx">ViewContainerRef</span><span class="p">;</span>
</code></pre></div></div>

<p>Si lo que queremos es cambiar toda la página, no hace falta entonces viewchild. solo habría que añadir en el constructor:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">constructor</span><span class="p">(</span>
    <span class="p">...</span>
    <span class="kr">private</span> <span class="nx">compDynamicContainer</span><span class="p">:</span> <span class="nx">ViewContainerRef</span><span class="p">,</span>
  <span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>

<h2 id="heading-2-fabricador-de-componentes-dinámicos-componentfactoryresolver">2. Fabricador de componentes dinámicos: ComponentFactoryResolver</h2>

<p>Esta parte tampoco ha cambiado. Inserta el servicio <code class="language-plaintext highlighter-rouge">ComponentFactoryResolver</code>, el cual permite fabricar componentes de forma dinámica.</p>

<p>En el constructor:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">constructor</span><span class="p">(</span>
    <span class="p">...</span>
    <span class="kr">private</span> <span class="nx">resolver</span><span class="p">:</span> <span class="nx">ComponentFactoryResolver</span>
  <span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>

<h2 id="heading-3-crear-factoría-y-visualizar-componente">3. Crear factoría y visualizar componente</h2>

<p>Es aquí donde cambia todo en Angular 9. Imagina 2 botones, dos funciones async que cada uno carga un componente distinto, y 2 botones, cada uno ejecuta una función async. Pues cambiando esos botones se cambiaría el componente, en la zona del dom donde refleja viewchild.</p>

<p>Crea una factoría para el componente dinámico,  que permita instanciarlo en el elemento contenedor creado anteriormente (compDynamicContainer).</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nx">ngAfterViewInit</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">getLazyComponent</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="k">async</span> <span class="nx">getLazyComponent</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">clear</span><span class="p">();</span>
    <span class="kd">const</span> <span class="p">{</span> <span class="nx">MiComponenteComponent</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="k">import</span><span class="p">(</span><span class="dl">'</span><span class="s1">@miscomponentes/components/mi-componente/mi-componente.component</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">MiComponenteComponent</span><span class="p">)</span>
    <span class="p">);</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>En este punto el componente se ha creado y se está visualizando. Si se quiere cargar en otro momento, incorporarlo en una función en vez de <code class="language-plaintext highlighter-rouge">ngAfterViewInit</code>.</p>

<h2 id="heading-4-acceso-a-los-métodos-y-propiedades-públicos">4. Acceso a los métodos y propiedades públicos</h2>

<p>Una vez guardada la referencia de la creación del componente, se puede acceder a dicho componente, ejecutar métodos públicos y acceder a sus propiedades.</p>

<p>Todo esto gracias a <code class="language-plaintext highlighter-rouge">instance</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">miFuncionSuma</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">titulo</span><span class="o">=</span><span class="dl">'</span><span class="s1">Cambio el titulo del componente</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">mensaje</span><span class="o">=</span><span class="dl">'</span><span class="s1">cambio el mensaje también</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>También se puede acceder e incorporar a cualquier @Input, ya que es público. 
En el caso de los @Output, nos podemos subscribir a ellos:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">miEventEmiter</span><span class="p">(()</span><span class="o">=&gt;</span><span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">se ejecuta el event emiter</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h2 id="heading-5-destruir-componente-dinámico">5. Destruir componente dinámico</h2>

<p>No ha cambiado nada de la última versión.</p>

<p>Supongamos que tenemos un contenedor <code class="language-plaintext highlighter-rouge">compDynamicContainer</code>, y queremos que en un primer momento se pinte un componente, pero que a los x segundos, o al pulsar cualquier acción, se elimine, o se cambie por otro componente. Esto puede ser si el componente dinámico es un modal, o tenemos un sistema de paginado con componentes dinámicos.</p>

<p>Esto se hace con <code class="language-plaintext highlighter-rouge">destroy()</code>. Un ejemplo:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ngAfterViewInit</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">getLazyComponent</span><span class="p">();</span> 

  <span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">getLazy2Component</span><span class="p">();</span>
  <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">async</span> <span class="nx">getLazyComponent</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">destroy</span><span class="p">();</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">clear</span><span class="p">();</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">miDinamicoComponent</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="k">import</span><span class="p">(</span><span class="dl">'</span><span class="s1">@miscomponentes/components/mi-componente/mi-componente.component</span><span class="dl">'</span><span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">AntsListComponent</span><span class="p">)</span>
  <span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">openCard</span><span class="p">(</span><span class="mi">24</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">async</span> <span class="nx">getLazy2Component</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">destroy</span><span class="p">();</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">clear</span><span class="p">();</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">miDinamicoComponent</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="k">import</span><span class="p">(</span><span class="dl">'</span><span class="s1">@miscomponentes/components/mi-componente/mi-componente2.component</span><span class="dl">'</span><span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">AntsListComponent</span><span class="p">)</span>
  <span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">card</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Angular" /><category term="Typescript" /><category term="Javascript" /><summary type="html"><![CDATA[Guia para hacer componentes dinámicos en Angular 9, una manera más rápida y limpia, con menos lineas y sin tocar los módulos.]]></summary></entry><entry><title type="html">Vuejs 3 - Primeros pasos - creación de proyecto, generación de componentes y directivas</title><link href="https://jolugama.com/blog/2019/05/05/vuejs3-primeros-pasos/" rel="alternate" type="text/html" title="Vuejs 3 - Primeros pasos - creación de proyecto, generación de componentes y directivas" /><published>2019-05-05T09:00:00+00:00</published><updated>2019-05-05T09:00:00+00:00</updated><id>https://jolugama.com/blog/2019/05/05/vuejs3-primeros-pasos</id><content type="html" xml:base="https://jolugama.com/blog/2019/05/05/vuejs3-primeros-pasos/"><![CDATA[<blockquote>
  <p>Guía de primeros pasos en vuejs 3</p>
</blockquote>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-1-prerrequisitos-e-instalación" id="markdown-toc-heading-1-prerrequisitos-e-instalación">1. Prerrequisitos e instalación</a>    <ul>
      <li><a href="#heading-11-instalación-vue-cli-3" id="markdown-toc-heading-11-instalación-vue-cli-3">1.1. instalación vue cli 3</a></li>
      <li><a href="#heading-12-creando-proyecto" id="markdown-toc-heading-12-creando-proyecto">1.2. Creando proyecto</a></li>
      <li><a href="#heading-13-ejecutando-app" id="markdown-toc-heading-13-ejecutando-app">1.3. Ejecutando app</a></li>
    </ul>
  </li>
  <li><a href="#heading-2-creando-componentes" id="markdown-toc-heading-2-creando-componentes">2. Creando componentes</a>    <ul>
      <li><a href="#heading-21-eventos-click-métodos-y-directivas-if-bind" id="markdown-toc-heading-21-eventos-click-métodos-y-directivas-if-bind">2.1. Eventos (click), métodos y directivas (if, bind)</a></li>
      <li><a href="#heading-22-single-file-component-y--separando-la-lógica-del-template" id="markdown-toc-heading-22-single-file-component-y--separando-la-lógica-del-template">2.2. Single file component y  separando la lógica del template</a></li>
      <li><a href="#heading-23-directiva-for" id="markdown-toc-heading-23-directiva-for">2.3. Directiva for</a></li>
    </ul>
  </li>
  <li><a href="#heading-3-plugins-visual-code" id="markdown-toc-heading-3-plugins-visual-code">3. Plugins visual code</a></li>
  <li><a href="#heading-4-linter--eslint" id="markdown-toc-heading-4-linter--eslint">4. linter : eslint</a></li>
  <li><a href="#heading-5-generador-de-componentes" id="markdown-toc-heading-5-generador-de-componentes">5. Generador de componentes</a></li>
</ul>

<h2 id="heading-1-prerrequisitos-e-instalación">1. Prerrequisitos e instalación</h2>

<p><a href="https://cli.vuejs.org/guide">Guía oficial de la versión 3.x</a></p>

<h3 id="heading-11-instalación-vue-cli-3">1.1. instalación vue cli 3</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> @vue/cli
<span class="c"># o</span>
<span class="nv">$ </span>yarn global add @vue/cli

</code></pre></div></div>

<p>Además instalamos los siguientes:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm i <span class="nt">-g</span> @vue/cli-plugin-babel @vue/cli-plugin-typescript @vue/cli-plugin-unit-mocha  eslint-plugin-vue @vue/cli-service
</code></pre></div></div>

<h3 id="heading-12-creando-proyecto">1.2. Creando proyecto</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vue create miapp

Elije manual <span class="k">select</span><span class="nb">.</span>  y añade las características que quieras
</code></pre></div></div>

<h3 id="heading-13-ejecutando-app">1.3. Ejecutando app</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm run serve

<span class="nv">$ </span>npx vue-cli-service serve
</code></pre></div></div>

<h2 id="heading-2-creando-componentes">2. Creando componentes</h2>

<p>Vue no dispone de un cli tan completo como tiene Angular. Es por ello que la generación de los componentes han de ser a mano.</p>

<h3 id="heading-21-eventos-click-métodos-y-directivas-if-bind">2.1. Eventos (click), métodos y directivas (if, bind)</h3>

<p>Se crea un objeto de tipo vue, donde se indica el nombre de el componente.</p>

<p>En <code class="language-plaintext highlighter-rouge">data</code> se almacena las variables scope que se necesitan en el html.</p>

<p>Un ejemplo de un componente, que dispone de su ts y el html.</p>

<p><strong>mi-componente.html</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">v-if=</span><span class="s">"mostrar"</span><span class="nt">&gt;</span>
    
    <span class="nt">&lt;img</span> <span class="na">v-bind:src=</span><span class="s">"imagen"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;img</span> <span class="na">:src=</span><span class="s">"imagen"</span> <span class="nt">/&gt;</span>
    <span class="c">&lt;!-- forma abreviada --&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;div</span> <span class="na">v-if=</span><span class="s">"mostrar"</span><span class="nt">&gt;</span>
  
  <span class="nt">&lt;img</span> <span class="na">:src=</span><span class="s">"imagen"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;button</span> <span class="na">v-on:click=</span><span class="s">"toggleMostrar"</span><span class="nt">&gt;</span>Mostrar/Ocultar<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;button</span> <span class="err">@</span><span class="na">click=</span><span class="s">"toggleMostrar"</span><span class="nt">&gt;</span>Mostrar/Ocultar<span class="nt">&lt;/button&gt;</span>
<span class="c">&lt;!-- forma abreviada --&gt;</span>
</code></pre></div></div>

<p><strong>mi-componente.ts</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
  <span class="na">el</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#app</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">mostrar</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">mensaje</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hola Vue!</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">imagen</span><span class="p">:</span>
      <span class="dl">"</span><span class="s2">http://.../una-imagen.jpg</span><span class="dl">"</span>
  <span class="p">},</span>
  <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">toggleMostrar</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">mostrar</span> <span class="o">=</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">mostrar</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>

<h3 id="heading-22-single-file-component-y--separando-la-lógica-del-template">2.2. Single file component y  separando la lógica del template</h3>

<ul>
  <li>Single file component: desde el propio archivo vue, incrustando el template.</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">app</span><span class="dl">"</span> <span class="o">/&gt;</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
  <span class="na">el</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#app</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="s2">`
    &lt;div&gt;
      &lt;div v-if="mostrar"&gt;
        
        &lt;img :src="imagen" /&gt;
      &lt;/div&gt;
      &lt;button @click="toggleMostrar"&gt;Mostrar/Ocultar&lt;/button&gt;
    &lt;/div&gt;
  `</span><span class="p">,</span>
  <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">mostrar</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">mensaje</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hola Vue!</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">imagen</span><span class="p">:</span>
      <span class="dl">"</span><span class="s2">http://.../una-imagen.jpg</span><span class="dl">"</span>
  <span class="p">},</span>
  <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">toggleMostrar</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">mostrar</span> <span class="o">=</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">mostrar</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>

<ul>
  <li>Usando <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> y separando el código del html</li>
</ul>

<p><strong>html</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;&lt;/div&gt;</span>
<span class="nt">&lt;template</span> <span class="na">id=</span><span class="s">"ejemplo"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">v-if=</span><span class="s">"mostrar"</span><span class="nt">&gt;</span>
      
      <span class="nt">&lt;img</span> <span class="na">:src=</span><span class="s">"imagen"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;button</span> <span class="err">@</span><span class="na">click=</span><span class="s">"toggleMostrar"</span><span class="nt">&gt;</span>Mostrar/Ocultar<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/template&gt;</span>
</code></pre></div></div>

<p><strong>vue</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
  <span class="na">el</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#app</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#ejemplo</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
    <span class="p">...</span>
  <span class="p">},</span>
  <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
    <span class="p">...</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="heading-23-directiva-for">2.3. Directiva for</h3>

<p><strong>vue</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
  <span class="na">el</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#app</span><span class="dl">"</span><span class="p">,</span>
  <span class="na">template</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#ejemplo</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">data</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">cursos</span><span class="p">:</span> <span class="p">[</span>
      <span class="p">{</span>
        <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">curso 1</span><span class="dl">"</span><span class="p">,</span>
        <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">http://.../una-imagen.jpg</span><span class="dl">"</span>
      <span class="p">},</span>
      <span class="p">{</span>
        <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">curso 2</span><span class="dl">"</span><span class="p">,</span>
        <span class="na">url</span><span class="p">:</span>
          <span class="dl">"</span><span class="s2">http://.../una-imagen.jpg</span><span class="dl">"</span>
      <span class="p">},</span>
      <span class="p">{</span>
        <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">curso 3</span><span class="dl">"</span><span class="p">,</span>
        <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">http://.../una-imagen.jpg</span><span class="dl">"</span>
      <span class="p">}</span>
    <span class="p">]</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>

<p><strong>html</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ul&gt;</span>
    <span class="nt">&lt;li</span> <span class="na">v-for=</span><span class="s">"(curso, index) in cursos"</span> <span class="na">:key=</span><span class="s">"index"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;a</span> <span class="na">:href=</span><span class="s">"curso.url"</span><span class="nt">&gt;&lt;/a&gt;</span>
    <span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;/ul&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<h2 id="heading-3-plugins-visual-code">3. Plugins visual code</h2>

<ul>
  <li>
    <p>Vue VS Code Extension Pack</p>
  </li>
  <li>
    <p>Vetur</p>
  </li>
</ul>

<h2 id="heading-4-linter--eslint">4. linter : eslint</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">-g</span> eslint
eslint <span class="nt">--init</span>

</code></pre></div></div>

<h2 id="heading-5-generador-de-componentes">5. Generador de componentes</h2>

<p><code class="language-plaintext highlighter-rouge">Vue</code> no tiene un cli tan bueno como el de <code class="language-plaintext highlighter-rouge">Angular</code>. Aún no dispone de generador de componentes.
Puedes probar esta biblioteca</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># $ npm install -g vue-generate-component</span>
<span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> vue-generate-component-typescript
</code></pre></div></div>

<p>Crea un componente:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vgc footer
</code></pre></div></div>

<p>y lo mueves a la carpeta correcta.</p>

<!-- Puedes cambiar los tipos de ficheros que genera, como el html, estilo, scripot y spec
```bash
$ vgc --html jade --style less --script ts --spec ts
``` -->

<!-- En este caso, conservamos el scss de los estilos, el html normal y script con typescript.

```bash
$ vgc --script ts --spec ts
``` -->]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Vuejs" /><category term="Typescript" /><category term="Javascript" /><summary type="html"><![CDATA[Guía de primeros pasos en vuejs 3]]></summary></entry><entry><title type="html">Angular 7 - Comunicación entre componentes, modificación del DOM y componentes dinámicos</title><link href="https://jolugama.com/blog/2019/03/30/comunicacion-componentes/" rel="alternate" type="text/html" title="Angular 7 - Comunicación entre componentes, modificación del DOM y componentes dinámicos" /><published>2019-03-30T09:00:00+00:00</published><updated>2019-03-30T09:00:00+00:00</updated><id>https://jolugama.com/blog/2019/03/30/comunicacion-componentes</id><content type="html" xml:base="https://jolugama.com/blog/2019/03/30/comunicacion-componentes/"><![CDATA[<blockquote>
  <p>Guía de buenas prácticas de comunicación entre componentes. Transclusión (ng-content), elementRef, componentes dinámicos, @ViewChild, @ContentChild, BehaviorSubject, Subject, Renderer2 …</p>
</blockquote>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-1-transclusión-con-ng-content-y-select-slots" id="markdown-toc-heading-1-transclusión-con-ng-content-y-select-slots">1. Transclusión con ng-content y select (slots)</a></li>
  <li><a href="#heading-2-comunicación-entre-componentes" id="markdown-toc-heading-2-comunicación-entre-componentes">2. Comunicación entre componentes</a>    <ul>
      <li><a href="#heading-21-comunicación-hijo-a-padre" id="markdown-toc-heading-21-comunicación-hijo-a-padre">2.1. Comunicación hijo a padre</a></li>
      <li><a href="#heading-22-comunicación-padre-a-hijo" id="markdown-toc-heading-22-comunicación-padre-a-hijo">2.2. Comunicación padre a hijo</a></li>
      <li><a href="#heading-23-emitiendo-eventos-desde-un-servicio" id="markdown-toc-heading-23-emitiendo-eventos-desde-un-servicio">2.3. Emitiendo eventos desde un servicio</a></li>
    </ul>
  </li>
  <li><a href="#heading-3-acceso-y-manipulación-del-dom" id="markdown-toc-heading-3-acceso-y-manipulación-del-dom">3 Acceso y manipulación del DOM</a>    <ul>
      <li><a href="#heading-31-mediante-elementref" id="markdown-toc-heading-31-mediante-elementref">3.1. Mediante elementRef</a></li>
      <li><a href="#heading-32-mediante-renderer" id="markdown-toc-heading-32-mediante-renderer">3.2. Mediante Renderer</a></li>
      <li><a href="#heading-33-directiva-ng-template" id="markdown-toc-heading-33-directiva-ng-template">3.3. Directiva ng-template</a></li>
      <li><a href="#heading-34-directiva-ng-container" id="markdown-toc-heading-34-directiva-ng-container">3.4. Directiva ng-container</a></li>
    </ul>
  </li>
  <li><a href="#heading-4-componentes-dinámicos" id="markdown-toc-heading-4-componentes-dinámicos">4. Componentes dinámicos</a>    <ul>
      <li><a href="#heading-41-indicar-que-componentes-son-dinámicos-en-el-módulo" id="markdown-toc-heading-41-indicar-que-componentes-son-dinámicos-en-el-módulo">4.1 Indicar que componentes son dinámicos en el módulo</a></li>
      <li><a href="#heading-42-indicar-lugar-donde-debería-cargar-el-componente-dinámico" id="markdown-toc-heading-42-indicar-lugar-donde-debería-cargar-el-componente-dinámico">4.2 Indicar lugar donde debería cargar el componente dinámico</a></li>
      <li><a href="#heading-43-fabricador-de-componentes-dinámicos-componentfactoryresolver" id="markdown-toc-heading-43-fabricador-de-componentes-dinámicos-componentfactoryresolver">4.3 Fabricador de componentes dinámicos: ComponentFactoryResolver</a></li>
      <li><a href="#heading-44-crear-factoría-y-visualizar-componente" id="markdown-toc-heading-44-crear-factoría-y-visualizar-componente">4.4 Crear factoría y visualizar componente</a></li>
      <li><a href="#heading-45-acceso-a-los-métodos-y-propiedades-públicos" id="markdown-toc-heading-45-acceso-a-los-métodos-y-propiedades-públicos">4.5 Acceso a los métodos y propiedades públicos</a></li>
      <li><a href="#heading-46-destruir-componente-dinámico" id="markdown-toc-heading-46-destruir-componente-dinámico">4.6 Destruir componente dinámico</a></li>
    </ul>
  </li>
</ul>

<h2 id="heading-1-transclusión-con-ng-content-y-select-slots">1. Transclusión con ng-content y select (slots)</h2>
<p>Inclusión de un componente o parte del mismo dentro de otro componente. En AngularJs lo conocemos por <code class="language-plaintext highlighter-rouge">ngTransclude</code>.
Un ejemplo claro es un componente modal que tiene unos estilos definidos, un título header, un texto body y una zona donde hay un número x de botones.</p>

<p>De esta manera nos ahoramos Outputs, eventEmitter y un complicado componente de condicionales para todas las casuísticas del proyecto, dejando que el componente padre que llame a dicho componente modal.</p>

<p>En este caso se usa select con atributos <code class="language-plaintext highlighter-rouge">[]</code>, aunque se puede usar mediante clases <code class="language-plaintext highlighter-rouge">.</code> o como un tag directamente.</p>

<p><strong>Componente modal</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"background"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"card"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"header"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ng-content</span> <span class="na">select=</span><span class="s">"[header]"</span><span class="nt">&gt;&lt;/ng-content&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"message"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ng-content</span> <span class="na">select=</span><span class="s">"[message]"</span><span class="nt">&gt;&lt;/ng-content&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"footer"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ng-content</span> <span class="na">select=</span><span class="s">"button"</span><span class="nt">&gt;&lt;/ng-content&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>Desde un componente padre, llama a la modal y dentro añade su propio header, body y botones. En este caso los estilos los maneja el padre, aplicando unos estilos diferentes a los definidos desde el componente modal.</p>

<p><strong>Componente padre</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;app-mi-modal</span> <span class="na">[hidden]=</span><span class="s">"!isModalVisible"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">header</span><span class="nt">&gt;</span>Esto es un título<span class="nt">&lt;/div&gt;</span>  
    <span class="nt">&lt;div</span> <span class="na">message</span><span class="nt">&gt;&lt;label&gt;</span>Este es el mensaje<span class="nt">&lt;/label&gt;&lt;/div&gt;</span>   
    <span class="nt">&lt;input</span> <span class="na">[(ngModel)]=</span><span class="s">"numero"</span> <span class="na">type=</span><span class="s">"number"</span> <span class="na">#numeroInput</span><span class="nt">&gt;</span> <span class="c">&lt;!-- template reference variable --&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">(click)=</span><span class="s">"cancelarModal()"</span> <span class="na">class=</span><span class="s">"cancel-button"</span><span class="nt">&gt;</span>Cancelar<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">(click)=</span><span class="s">"aceptarModal()"</span> <span class="na">class=</span><span class="s">"ok-button"</span><span class="nt">&gt;</span>Aceptar<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;/app-mi-modal&gt;</span>
</code></pre></div></div>

<h2 id="heading-2-comunicación-entre-componentes">2. Comunicación entre componentes</h2>

<p>Para la comunicación entre distintos componentes, hay que seguir distintas estrategias según las posiciones entre ellos.
Por ello no es lo mismo tratar un componente padre a un hijo, y viceversa.</p>

<p>A parte de los <code class="language-plaintext highlighter-rouge">property binding</code> mediante decorador @Input, <code class="language-plaintext highlighter-rouge">event binding</code> mediante decorador @Output, y los servicios con provider en módulo, angular nos proporciona una serie de técnicas que optimizan mucho el código.</p>

<h3 id="heading-21-comunicación-hijo-a-padre">2.1. Comunicación hijo a padre</h3>

<ul>
  <li>Mediante inyección de dependencias.
El padre dispone de unos métodos públicos y estos pueden ser usados desde el hijo. No es muy recomendable esta opción, mejor usar comunicación de padre a hijo mediante <code class="language-plaintext highlighter-rouge">@ContentChild</code> o <code class="language-plaintext highlighter-rouge">@ContentChildren</code>.</li>
</ul>

<p><strong>Componente padre</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">FatherComponent</span> <span class="p">{</span>

  <span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>

  <span class="nx">suma</span><span class="p">(</span><span class="nx">num1</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">num2</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">num1</span> <span class="o">+</span> <span class="nx">num2</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">resta</span><span class="p">(</span><span class="nx">num1</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">num2</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">num1</span> <span class="o">-</span> <span class="nx">num2</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div></div>

<p><strong>Componente hijo</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">FatherComponent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../FatherComponent/FatherComponent.component</span><span class="dl">'</span><span class="p">;</span>

<span class="p">...</span>

<span class="k">export</span> <span class="kd">class</span> <span class="nx">ChildrenComponent</span> <span class="p">{</span>

  <span class="kd">constructor</span><span class="p">(</span><span class="k">public</span> <span class="nx">father</span><span class="p">:</span> <span class="nx">FatherComponent</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>

  <span class="nx">miMetodo</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">father</span><span class="p">.</span><span class="nx">suma</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">father</span><span class="p">.</span><span class="nx">resta</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-22-comunicación-padre-a-hijo">2.2. Comunicación padre a hijo</h3>

<ul>
  <li>mediante atributos con decoradores @Input y @Output.</li>
  <li>mediante <code class="language-plaintext highlighter-rouge">template reference variable</code> (desde html)</li>
</ul>

<p>Permite referenciar un componente hijo, y llamarlo desde el propio template, teniendo acceso a todos sus métodos públicos.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;app-mi-componente</span> <span class="na">#micomponente</span><span class="nt">&gt;&lt;/app-mi-componente&gt;</span>
<span class="nt">&lt;button</span> <span class="na">(click)=</span><span class="s">"micomponente.metodoPublico()"</span><span class="nt">&gt;</span>mi botón<span class="nt">&lt;/button&gt;</span>
</code></pre></div></div>

<ul>
  <li>mediante <code class="language-plaintext highlighter-rouge">@ContentChild</code>. con componente dentro de un <code class="language-plaintext highlighter-rouge">ng-content</code>.
Usar ContentChild para obtener el primer elemento o la directiva que coincida con el selector del contenido DOM. Si el contenido del DOM cambia y un nuevo elemento secundario coincide con el selector, la propiedad se actualizará.</li>
</ul>

<p>Es más potente que <code class="language-plaintext highlighter-rouge">template reference variable</code>, ya que permite acceder aparte del template, desde el propio typescript, pudiendo usar los métodos públicos del hijo desde el padre.</p>

<p><strong>Importante</strong> No se puede tener @ContentChild desde el padre y inyección de dependencia desde el hijo al padre, la cual hace una dependencia circular, la cual no se puede resolver.</p>

<p>El contenido de un componente no está disponible para su padre hasta despues de su inicialización. Es por ello que se debe de utilizar el evento de ciclo de vida <code class="language-plaintext highlighter-rouge">ngAfterContentInit()</code>;</p>

<ul>
  <li>mediante <code class="language-plaintext highlighter-rouge">@ContentChildren</code> si hay varios componentes iguales y están dentro de un <code class="language-plaintext highlighter-rouge">ng-content</code>. Hay que usar <code class="language-plaintext highlighter-rouge">QueryList</code>.</li>
</ul>

<p><strong>En el ts</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">FatherComponent</span> <span class="k">implements</span> <span class="nx">OnInit</span><span class="p">,</span> <span class="nx">AfterContentInit</span><span class="p">,</span> <span class="nx">OnDestroy</span> <span class="p">{</span>

  <span class="p">@</span><span class="nd">ContentChildren</span><span class="p">(</span><span class="nx">ChildComponent</span><span class="p">)</span> <span class="k">public</span> <span class="nx">myChildren</span><span class="p">:</span><span class="nx">QueryList</span><span class="o">&lt;</span><span class="nx">ChildComponent</span><span class="o">&gt;</span><span class="p">;</span>

  <span class="nx">ngAfterContentInit</span><span class="p">(){</span>
    <span class="c1">// llamar a los métodos de myChildren </span>
  <span class="p">}</span>

</code></pre></div></div>

<p><strong>En el html</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"content"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ng-content&gt;&lt;/ng-content&gt;</span>
<span class="nt">&lt;/div&gt;</span> 
</code></pre></div></div>

<ul>
  <li>
    <p>mediante <code class="language-plaintext highlighter-rouge">@ViewChild</code>. Es como <code class="language-plaintext highlighter-rouge">@ContentChild</code> pero sin estar dentro de <code class="language-plaintext highlighter-rouge">ng-content</code>, directamente en el template del padre. Es decir, es como una <code class="language-plaintext highlighter-rouge">template reference variable</code> pero del lado del typescript.</p>
  </li>
  <li>
    <p>mediante <code class="language-plaintext highlighter-rouge">@ViewChildren</code> pero para varios componentes iguales.</p>
  </li>
</ul>

<h3 id="heading-23-emitiendo-eventos-desde-un-servicio">2.3. Emitiendo eventos desde un servicio</h3>

<p>Cuando aumenta la lógica de un componente, a menudo la trasladamos a un servicio. Si se tiene un eventEmitter, desde el servicio no se puede hacer de la misma manera. Hay que crear un observable y desde el componente subscribirse para pasar el eventEmitter.</p>

<ul>
  <li>Si se va a emitir sin valor alguno:</li>
</ul>

<p><strong>servicio</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Subject es un tipo especial de observable que permite que los valores se compartan a muchos observadores.</span>
<span class="k">private</span> <span class="nx">clickadoSource</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Subject</span><span class="o">&lt;</span><span class="k">void</span><span class="o">&gt;</span><span class="p">();</span> 
<span class="k">public</span> <span class="nx">clickObs$</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">clickadoSource</span><span class="p">.</span><span class="nx">asObservable</span><span class="p">();</span>

<span class="p">...</span>

<span class="k">private</span> <span class="nx">clickar</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">clickadoSource</span><span class="p">.</span><span class="nx">next</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Si se emite aportando un valor:</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BehaviorSubject es como Subject, pero además se le envía un parámetro.</span>
<span class="k">private</span> <span class="nx">numeroSource</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BehaviorSubject</span><span class="o">&lt;</span><span class="kr">number</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">55</span><span class="p">);</span>
<span class="k">public</span> <span class="nx">cambiaNumObs$</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">numeroSource</span><span class="p">.</span><span class="nx">asObservable</span><span class="p">();</span>

<span class="p">...</span>

<span class="k">private</span> <span class="nx">cambiaNum</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// BehaviorSubject tiene métodos, entre ellos el de recuperación de valor</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">numeroSource</span><span class="p">.</span><span class="nx">next</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">numeroSource</span><span class="p">.</span><span class="nx">getValue</span><span class="p">()</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> 
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Componente que llama al servicio</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">this</span><span class="p">.</span><span class="nx">aSubscription</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">miServicio</span><span class="p">.</span><span class="nx">clickObs$</span>
  <span class="p">.</span><span class="nx">subscribe</span><span class="p">(()</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">miEventEmmiter</span><span class="p">.</span><span class="nx">emit</span><span class="p">();</span>
<span class="p">});</span>

<span class="k">this</span><span class="p">.</span><span class="nx">bSubscription</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">miServicio</span><span class="p">.</span><span class="nx">cambiaNumObs$</span>
  <span class="p">.</span><span class="nx">subscribe</span><span class="p">((</span><span class="nx">data</span><span class="p">)</span><span class="o">=&gt;</span><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">miEventEmmiter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h2 id="heading-3-acceso-y-manipulación-del-dom">3 Acceso y manipulación del DOM</h2>

<h3 id="heading-31-mediante-elementref">3.1. Mediante elementRef</h3>

<p>El elementRef es una referencia que se obtiene a partir de un elemento generado con viewChild o contentChild.
Una vez creada la <code class="language-plaintext highlighter-rouge">ViewChild</code> de tipo <code class="language-plaintext highlighter-rouge">ElementRef</code>, se puede manipular el dom desde el método <code class="language-plaintext highlighter-rouge">ngAfterViewInit</code>.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;input</span> <span class="na">[(ngModel)]=</span><span class="s">"time"</span> <span class="na">type=</span><span class="s">"number"</span> <span class="na">#miInput</span><span class="nt">&gt;</span>
</code></pre></div></div>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">@</span><span class="nd">ViewChild</span><span class="p">(</span><span class="dl">"</span><span class="s2">miInput</span><span class="dl">"</span><span class="p">)</span> <span class="nx">nombre</span><span class="p">:</span> <span class="nx">ElementRef</span><span class="p">;</span>

  <span class="nx">ngAfterViewInit</span><span class="p">(){</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">);</span> <span class="c1">// aparece la propiedad nativeElement y dentro una gran cantidad de propiedades y métodos</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">placeholder</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Escriba su nombre</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">.</span><span class="nx">addClass</span><span class="p">(</span><span class="dl">'</span><span class="s1">una-clase</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">.</span><span class="nx">focus</span><span class="p">();</span>
  <span class="p">}</span>

</code></pre></div></div>

<h3 id="heading-32-mediante-renderer">3.2. Mediante Renderer</h3>

<p>Angular es platform agnostic, permite renderizar en varias plataformas que no sea navegador web, como es el caso de los Web Workers.
Es por ello que se debe evitar a toda costa el uso de las variables globales <code class="language-plaintext highlighter-rouge">window</code>, <code class="language-plaintext highlighter-rouge">document</code>, o manipular el DOM con <code class="language-plaintext highlighter-rouge">ElementRef</code>.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">constructor</span><span class="p">(</span><span class="k">private</span> <span class="nx">renderer</span><span class="p">:</span> <span class="nx">Renderer2</span><span class="p">)</span> <span class="p">{</span> 
  <span class="p">}</span>

  <span class="nx">ngAfterViewInit</span><span class="p">(){</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">renderer</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">,</span> <span class="dl">"</span><span class="s2">placeholder</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Escriba su nombre</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">renderer</span><span class="p">.</span><span class="nx">addClass</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">,</span> <span class="dl">'</span><span class="s1">una-clase</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">renderer</span><span class="p">.</span><span class="nx">selectRootElement</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">nombre</span><span class="p">.</span><span class="nx">nativeElement</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span>
  <span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-33-directiva-ng-template">3.3. Directiva ng-template</h3>

<p>Mediante este etiqueta, agrupa código html que se puede reutilizar sucesivamente.</p>

<p><strong>Una forma</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">*ngIf=</span><span class="s">"datos else loading"</span><span class="nt">&gt;</span>
  ... 
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#loading</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div&gt;</span>Loading...<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<p><strong>Otra forma</strong></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ng-template</span> <span class="na">[ngIf]=</span><span class="s">"datos"</span> <span class="na">[ngIfElse]=</span><span class="s">"loading"</span><span class="nt">&gt;</span>
   <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"una-clase"</span><span class="nt">&gt;</span>
     ... 
   <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>

<span class="nt">&lt;ng-template</span> <span class="na">#loading</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div&gt;</span>Loading...<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>
</code></pre></div></div>

<p>Sin utilizar directivas <code class="language-plaintext highlighter-rouge">*ngIf</code>, mediante directiva <code class="language-plaintext highlighter-rouge">ngTemplateOutlet</code>:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ng-template</span> <span class="na">#saluda</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div&gt;</span>Hola!!!<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/ng-template&gt;</span>

<span class="nt">&lt;ng-container</span> <span class="na">[ngTemplateOutlet]=</span><span class="s">"saluda"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;ng-container</span> <span class="na">[ngTemplateOutlet]=</span><span class="s">"saluda"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
<span class="nt">&lt;ng-container</span> <span class="na">[ngTemplateOutlet]=</span><span class="s">"saluda"</span><span class="nt">&gt;&lt;/ng-container&gt;</span>

</code></pre></div></div>

<h3 id="heading-34-directiva-ng-container">3.4. Directiva ng-container</h3>

<p>Al poner un <code class="language-plaintext highlighter-rouge">*ngIf</code> se crea en el dom un div inecesario. Actua como un nodo del DOM pero que en realidad no se renderiza. La forma de poder evitarlo es así.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ng-container</span> <span class="na">*ngIf=</span><span class="s">"visible"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"una-clase"</span><span class="nt">&gt;&lt;/div&gt;</span>
  ...
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/ng-container&gt;</span>
</code></pre></div></div>

<h2 id="heading-4-componentes-dinámicos">4. Componentes dinámicos</h2>

<h3 id="heading-41-indicar-que-componentes-son-dinámicos-en-el-módulo">4.1 Indicar que componentes son dinámicos en el módulo</h3>

<p>Esto se hace desde el módulo donde se hacen las declarations. Aclaro, si por ejemplo tenemos en el módulo shared el componente donde va a cargar x componentes dinámicos, sería shared.module.ts.
Se debe añadir a <code class="language-plaintext highlighter-rouge">entryComponents</code> todos los componentes dinámicos</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">declarations</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">miDinamicoComponent</span><span class="p">,</span>
    <span class="nx">miDinamicoComponent2</span>
<span class="p">],</span>
<span class="nx">imports</span><span class="p">:</span> <span class="p">[</span>
    <span class="p">...</span>
<span class="p">],</span>
<span class="nx">entryComponents</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">miDinamicoComponent</span><span class="p">,</span>
    <span class="nx">miDinamicoComponent2</span>
<span class="p">]</span>
</code></pre></div></div>

<p>De esta manera, angular sabe que en algún momento este componente se va a incluir al dom de forma dinámica.</p>

<h3 id="heading-42-indicar-lugar-donde-debería-cargar-el-componente-dinámico">4.2 Indicar lugar donde debería cargar el componente dinámico</h3>

<p>Justo donde se quiere añadir dicho componente dinámico, añadir una <code class="language-plaintext highlighter-rouge">template reference variable</code> para tener una referencia al elemento.</p>

<p>En el html:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
<span class="nt">&lt;ng-container</span> <span class="na">#componenteDinamico</span><span class="nt">&gt;&lt;/ng-container&gt;</span>
</code></pre></div></div>

<p>Podría ser un div, pero se crearía un padre div estuviera creado o no dicho componente dinámico. Para eliminar ese div innecesario.</p>

<p>Todo lo demás se hace en el ts.</p>
<ul>
  <li>Importa los componentes a referenciar.</li>
  <li>Crea una referencia a este elemento con <code class="language-plaintext highlighter-rouge">ViewChild</code> de tipo <code class="language-plaintext highlighter-rouge">ViewContainerRef</code>.</li>
</ul>

<p>En el ts:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">ItemsListComponent</span> <span class="kr">implements</span> <span class="nx">OnInit</span><span class="p">,</span> <span class="nx">OnDestroy</span><span class="p">,</span> <span class="nx">AfterViewInit</span> <span class="p">{</span>
  <span class="nl">miFactory</span><span class="p">:</span> <span class="nx">ComponentFactory</span><span class="o">&lt;</span><span class="nx">any</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="nl">componentRef</span><span class="p">:</span> <span class="nx">ComponentRef</span><span class="o">&lt;</span><span class="nx">miDinamicoComponent</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// se declara una variable referencia.</span>
  <span class="nl">componentRef2</span><span class="p">:</span> <span class="nx">ComponentRef</span><span class="o">&lt;</span><span class="nx">miDinamicoComponent2</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// se declara una variable referencia.</span>
  <span class="c1">// ViewContainerRef crea componentes en su interior de forma dinámica</span>
  <span class="c1">// Hay que añadir read: ViewContainerRef, ya que si no, devolvería un ElementRef,</span>
  <span class="c1">// que es lo que devuelve por defecto un viewChild.</span>
  <span class="c1">// la referencia que tenemos es compDynamicContainer, y en ese contenedor añadiremos los componentes dinámicos</span>
  <span class="p">@</span><span class="nd">ViewChild</span><span class="p">(</span><span class="dl">'</span><span class="s1">componenteDinamico</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">read</span><span class="p">:</span> <span class="nx">ViewContainerRef</span> <span class="p">})</span> <span class="nx">compDynamicContainer</span><span class="p">:</span> <span class="nx">ViewContainerRef</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="heading-43-fabricador-de-componentes-dinámicos-componentfactoryresolver">4.3 Fabricador de componentes dinámicos: ComponentFactoryResolver</h3>

<p>Insertar el servicio <code class="language-plaintext highlighter-rouge">ComponentFactoryResolver</code>, el cual permite fabricar componentes de forma dinámica</p>

<p>En el constructor:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">constructor</span><span class="p">(</span>
    <span class="p">...</span>
    <span class="kr">private</span> <span class="nx">resolver</span><span class="p">:</span> <span class="nx">ComponentFactoryResolver</span>
  <span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</code></pre></div></div>

<h3 id="heading-44-crear-factoría-y-visualizar-componente">4.4 Crear factoría y visualizar componente</h3>

<p>Crea una factoría para el componente dinámico,  que permita instanciarlo en el elemento contenedor creado anteriormente (compDynamicContainer).</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nx">ngAfterViewInit</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">miFactory</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">miDinamicoComponent</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span><span class="nx">miFactory</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>En este punto el componente se ha creado y se está visualizando. Si se quiere cargar en otro momento, incorporarlo en una función en vez de <code class="language-plaintext highlighter-rouge">ngAfterViewInit</code>.</p>

<h3 id="heading-45-acceso-a-los-métodos-y-propiedades-públicos">4.5 Acceso a los métodos y propiedades públicos</h3>

<p>Una vez guardada la referencia de la creación del componente, se puede acceder a dicho componente, ejecutar métodos públicos y acceder a sus propiedades.</p>

<p>Todo esto gracias a <code class="language-plaintext highlighter-rouge">instance</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">miFuncionSuma</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">titulo</span><span class="o">=</span><span class="dl">'</span><span class="s1">Cambio el titulo del componente</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">mensaje</span><span class="o">=</span><span class="dl">'</span><span class="s1">cambio el mensaje también</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>También se puede acceder e incorporar en un @Input, ya que es público. 
En el caso de los @Output, nos podemos subscribir a ellos:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">miEventEmiter</span><span class="p">(()</span><span class="o">=&gt;</span><span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">se ejecuta el event emiter</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h3 id="heading-46-destruir-componente-dinámico">4.6 Destruir componente dinámico</h3>

<p>Supongamos que tenemos un contenedor <code class="language-plaintext highlighter-rouge">compDynamicContainer</code>, y queremos que en un primer momento se pinte un componente, pero que a los x segundos, o al pulsar cualquier acción, se elimine, o se cambie por otro componente. Esto puede ser si el componente dinámico es un modal, o tenemos un sistema de paginado con componentes dinámicos.</p>

<p>Esto se hace con <code class="language-plaintext highlighter-rouge">destroy()</code>. Un ejemplo:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">ngAfterViewInit</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">miFactory</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">miDinamicoComponent</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">miFactory</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">instance</span><span class="p">.</span><span class="nx">openCard</span><span class="p">(</span><span class="mi">24</span><span class="p">);</span>

  <span class="c1">// en 2 segundos destruimos el componente 1 e incorporamos el componente 2.</span>
    <span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">componentRef</span><span class="p">.</span><span class="nx">destroy</span><span class="p">();</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">miFactory</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">resolver</span><span class="p">.</span><span class="nx">resolveComponentFactory</span><span class="p">(</span><span class="nx">miDinamicoComponent2</span><span class="p">);</span>
      <span class="c1">// En la referencia componentRef, creamos el componente dinámico dentro del contenedor compDynamicContainer</span>
      <span class="c1">// el tipo de componente dinámico lo asigna la factoría</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">componentRef2</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">compDynamicContainer</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">miFactory</span><span class="p">);</span>
    <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div></div>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Angular" /><category term="Typescript" /><category term="Javascript" /><summary type="html"><![CDATA[Guía de buenas prácticas de comunicación entre componentes. Transclusión (ng-content), elementRef, componentes dinámicos, @ViewChild, @ContentChild, BehaviorSubject, Subject, Renderer2 ...]]></summary></entry><entry><title type="html">Swagger-codegen - Genera interfaces de API Swagger a Angular</title><link href="https://jolugama.com/blog/2019/03/20/swagger-codegen-typescript/" rel="alternate" type="text/html" title="Swagger-codegen - Genera interfaces de API Swagger a Angular" /><published>2019-03-20T09:00:00+00:00</published><updated>2019-03-20T09:00:00+00:00</updated><id>https://jolugama.com/blog/2019/03/20/swagger-codegen-typescript</id><content type="html" xml:base="https://jolugama.com/blog/2019/03/20/swagger-codegen-typescript/"><![CDATA[<blockquote>
  <p>Tutorial para obtener todas las interfaces de un API generado por swagger y expórtalo de forma instantanea en un proyecto Angular.</p>
</blockquote>

<ul class="toc" id="markdown-toc">
  <li><a href="#heading-1-qué-es-swagger" id="markdown-toc-heading-1-qué-es-swagger">1. ¿Qué es Swagger?</a></li>
  <li><a href="#heading-2-diferencias-entre-swagger-y-openapi" id="markdown-toc-heading-2-diferencias-entre-swagger-y-openapi">2. Diferencias entre Swagger y openApi</a></li>
  <li><a href="#heading-3-descarga-swagger-codegen" id="markdown-toc-heading-3-descarga-swagger-codegen">3. Descarga swagger-codegen</a></li>
  <li><a href="#heading-4-ejemplo-de-api" id="markdown-toc-heading-4-ejemplo-de-api">4. Ejemplo de api</a></li>
  <li><a href="#heading-5-generar-código-con-swagger-codegen" id="markdown-toc-heading-5-generar-código-con-swagger-codegen">5. Generar código con swagger-codegen</a></li>
  <li><a href="#heading-6-publicar-el-package-a-npm" id="markdown-toc-heading-6-publicar-el-package-a-npm">6. Publicar el package a NPM</a></li>
  <li><a href="#heading-7-referencias" id="markdown-toc-heading-7-referencias">7. Referencias</a></li>
</ul>

<p>Mediante la herramienta swagger-codegen, generamos todas las interfaces de un API que disponga de swagger en un solo click, rápido y sencillo. Si no tienes ninguna ahora no te preocupes, usaremos una gratuita online.</p>

<p>Swagger Codegen te ayuda en la genereción automática de modelo y servicios, olvida escribirlo a mano.</p>

<h2 id="heading-1-qué-es-swagger">1. ¿Qué es Swagger?</h2>

<p>Swagger es el marco de herramientas más grande del mundo para la especificación OpenAPI (OEA). Es el estándar de facto para las API reutilizables y mantenibles. El conjunto de herramientas facilita enormemente el dolor de documentar e interactuar con las API.</p>

<h2 id="heading-2-diferencias-entre-swagger-y-openapi">2. Diferencias entre Swagger y openApi</h2>

<p>OpenAPI = Especificación.</p>

<p>Swagger = Herramientas para implementar la especificación.</p>

<h2 id="heading-3-descarga-swagger-codegen">3. Descarga swagger-codegen</h2>

<p>La lista de lenguajes y marcos compatibles está creciendo constantemente: En nuestro caso, nos interesesa el generador de código <code class="language-plaintext highlighter-rouge">typescript-angular</code>.</p>

<p>Necesitas el generador compilado: <code class="language-plaintext highlighter-rouge">swagger-codegen-cli.jar</code>.</p>

<p>Para descargar la última versión: <a href="http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/">swagger-codegen-cli</a></p>

<p>Para más informacion: <a href="https://github.com/swagger-api/swagger-codegen">github.com/swagger-api/swagger-codegen</a></p>

<h2 id="heading-4-ejemplo-de-api">4. Ejemplo de api</h2>

<p>En este artículo, usaremos el siguiente api, que tiene su propio swagger:
<a href="https://api.angular.schule/">api.angular.schule</a></p>

<p>Lo puedes explorar via <a href="https://api.angular.schule/swagger-ui/">Swagger UI</a></p>

<h2 id="heading-5-generar-código-con-swagger-codegen">5. Generar código con swagger-codegen</h2>

<p>Swagger-codegen dispone de muchos argumentos, los mínimos son:</p>

<p>Tan solo necesitas indicar el jar de codegen-cli, la url donde se aloja swagger.json, indicar que el tipo de generado es typescript-angular y la ruta destino es x.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>java <span class="nt">-jar</span> ./swagger-codegen-cli-2.4.1.jar generate <span class="nt">-i</span> https://api.angular.schule/swagger.json <span class="nt">-l</span> typescript-angular  <span class="nt">-o</span> ./angular_api_client
</code></pre></div></div>

<p>Con esto ya tendrás una nueva carpeta <code class="language-plaintext highlighter-rouge">angular_api_client</code> con todos los modelos del api, junto a sus servicios.</p>

<h2 id="heading-6-publicar-el-package-a-npm">6. Publicar el package a NPM</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm init
npm <span class="nb">install
</span>npm run build
npm publish dist <span class="nt">--access</span><span class="o">=</span>public
npm <span class="nb">install </span>nombreDelNpmInit <span class="nt">--save</span>
</code></pre></div></div>

<h2 id="heading-7-referencias">7. Referencias</h2>

<p><a href="https://angular.schule/blog/2018-04-swagger-codegen">angular.schule/blog/2018-04-swagger-codegen</a></p>]]></content><author><name>José Luis García Martínez</name><email>joseluis@jolugama.com</email></author><category term="Tutorial" /><category term="Herramientas" /><category term="Angular" /><category term="Typescript" /><category term="Javascript" /><summary type="html"><![CDATA[Tutorial para obtener todas las interfaces de un API generado por swagger y expórtalo de forma instantanea en un proyecto Angular.]]></summary></entry></feed>