<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Woodruff</title>
    <description>The latest articles on DEV Community by John Woodruff (@johnbwoodruff).</description>
    <link>https://dev.to/johnbwoodruff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1024%2Fbdf12b1f-70b2-4dba-9d47-ad8a375c87c7.jpg</url>
      <title>DEV Community: John Woodruff</title>
      <link>https://dev.to/johnbwoodruff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnbwoodruff"/>
    <language>en</language>
    <item>
      <title>PCIe Passthrough with Cloud-Init in Proxmox VE</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 30 Jul 2023 05:07:16 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/pcie-passthrough-with-cloud-init-in-proxmox-ve-250i</link>
      <guid>https://dev.to/johnbwoodruff/pcie-passthrough-with-cloud-init-in-proxmox-ve-250i</guid>
      <description>&lt;p&gt;As I wrote about in my &lt;a href="https://johnbwoodruff.com/posts/homelab/"&gt;last post&lt;/a&gt; I have been diving into the world of homelabs and the fun and sometimes frustrating world of hosting your own services on your own hardware in your basement. I have been using Proxmox VE as my hypervisor of choice and have been loving it. That being said there are often times where I need to cobble together information from various sources to figure something out, whether it be the Proxmox forums, blog posts, or YouTube videos by a variety of homelabbers.&lt;/p&gt;

&lt;p&gt;I recently purchased a used Nvidia Quadro P2000 GPU on Ebay for my server to utilize for Plex hardware transcoding as my server's CPU doesn't have an iGPU so watching stuff on Plex always ended up being endless buffering. I finally got it this last week and popped it in my server and began the long arduous task of figuring out how to utilize PCIe Passthrough in Proxmox to be able to utilize that GPU in my Plex virtual machine. Thankfully, there are some excellent tutorials out there on the subject, including &lt;a href="https://youtu.be/-HCzLhnNf-A"&gt;this one&lt;/a&gt; by Craft Computing which got me 90% of the way there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I will fully admit that this is a problem of my own making. I very recently followed &lt;a href="https://technotim.live/posts/cloud-init-cloud-image/"&gt;this tutorial&lt;/a&gt; by TechnoTim on using cloud-init in Proxmox to create virtual machines using Ubuntu's cloud images. Super awesome, I love the idea, and so naturally I had to set up Plex using a new cloud-init template. It was all going swimmingly until I tried to add my GPU to the VM in Proxmox, and suddenly when I tried to boot the VM it would just hang forever.&lt;/p&gt;

&lt;p&gt;Most other smart people probably would've just said "no worries, I'll just use Plex in a VM that doesn't use cloud-init and call it a day" but instead I decided I would rather spend hours diving down rabbit holes to figure it out. Thankfully, in combination with multiple forum threads, blog posts, random videos, and super friendly community members in the &lt;a href="https://l.technotim.live/discord"&gt;TechnoTim Discord&lt;/a&gt;, I was able to get it working.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The above tutorial is fantastic for setting up an awesome, clean, usable cloud-init template. I'm going to outline the changes I made that differ from that setup so that anyone else having this issue can clearly see the changes I made and hopefully get it working for themselves. The super short answer is that I needed it to be a UEFI boot setup with a few related changes to allow GPU passthrough, but here are the specific details.&lt;/p&gt;

&lt;p&gt;The first issue is that when I was setting up the cloud-init drive rather than using &lt;code&gt;ide2&lt;/code&gt; I needed to use &lt;code&gt;scsi1&lt;/code&gt;. I got that information from this Proxmox Forum &lt;a href="https://forum.proxmox.com/threads/cloud-init-image-only-applies-configuration-on-second-boot.93414/post-454108"&gt;thread comment&lt;/a&gt; So I ran the following (replacing &lt;code&gt;{VMID}&lt;/code&gt; with my actual VMID):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;qm &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;VMID&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--scsi1&lt;/span&gt; local-lvm:cloudinit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I finished the commands in the above tutorial, I made some further changes to the VM before making it a template. The first is that I changed the BIOS to &lt;code&gt;OVMF (UEFI)&lt;/code&gt;. In order to support this, I needed to add an EFI Disk. I added that from the hardware menu, selected my storage target, and then SUPER IMPORTANTLY you need to UNCHECK the "Pre-Enroll keys" checkbox. This is a confusing name but basically this disables Secure Boot. If you have this enabled, then your Nvidia drivers won't work. I spent a substantial amount of time trying to figure out why my Nvidia driver wasn't working, and it wasn't until a super friendly person in TechnoTim's Discord server pointed me to that setting that I got this all working.&lt;/p&gt;

&lt;p&gt;Another change I needed to make in order to be able to correctly pass through the GPU is in the hardware menu in Proxmox, "Machine" was set to the default of &lt;code&gt;i440fx&lt;/code&gt;, and it needed to be set to &lt;code&gt;q35&lt;/code&gt;. That would allow me to select the "PCI-Express" checkbox when adding a GPU via the Add PCI Device menu. I then had my virtual hardware set up so I could create a template from this VM.&lt;/p&gt;

&lt;p&gt;Finally, once I had booted a fresh VM from the above template, I had some annoyance getting the Nvidia drivers installed. In the end, as I mentioned above, it was simply because I didn't have my Secure Boot disabled, but I was able to find an easier way to install Nvidia drivers than downloading the &lt;code&gt;.run&lt;/code&gt; file and installing it manually. You can merely search &lt;code&gt;apt&lt;/code&gt; for nvidia drivers by running &lt;code&gt;apt search nvidia-driver&lt;/code&gt; which should list a large number of driver versions. You install the version your card requires, which you can find out by going to Nvidia's website and look up your card to see the version of the driver it needs to use. In my case it was version 535, so I very easily installed the drivers by using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nvidia-driver-535 nvidia-dkms-535
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point everything worked beautifully! I was able to pass through my GPU, got it hooked up to Plex, and tested it to see the minimal CPU usage and hardware GPU transcoding in action!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sometimes virtualization can be intimidating, frustrating, and difficult to figure out. On the other hand, it's very rewarding when you're able to get something working that has been troublesome for you. In this case I am super excited to be able to better utilize my Plex installation to watch my content, and while it was tough to get it working just right, I now understand a lot more around virtualization, cloud-init, and UEFI. That's the whole point of my homelab. Besides the fun I have messing around with stuff, I have already learned a ton about technology that has helped me both personally and in my career.&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>proxmox</category>
      <category>virtualization</category>
    </item>
    <item>
      <title>My Homelab</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 22 Jan 2023 04:30:02 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/my-homelab-11mb</link>
      <guid>https://dev.to/johnbwoodruff/my-homelab-11mb</guid>
      <description>&lt;p&gt;I have a variety of hobbies, but the most obvious one is playing around with fun technologies. This applies not only to software, which I obviously write for a living and for enjoyment, but also to hardware. Ever since I took apart my parents' VHS player to see how it worked (RIP that VHS player 🪦) I have loved tinkering with hardware, and have since then built multiple PCs, wired my home for ethernet, installed security cameras, and more. The hardware projects I've most recently spent my time doing has been assembling my homelab in my basement, and that's what this post is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a homelab?
&lt;/h2&gt;

&lt;p&gt;Before we dive into my specific homelab, we should cover what a homelab is for those who have never heard the term. It's a very broad inclusive term that refers to some sort of setup in your house that you can use to tinker, mess around with software or hardware, and generally experiment in the safe confines of your own home and network. This homelab can be as tiny as a single &lt;a href="https://www.raspberrypi.com/products/" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt; or old laptop, to something as big as a &lt;a href="https://www.reddit.com/r/HomeDataCenter/" rel="noopener noreferrer"&gt;Home Data Center&lt;/a&gt;. The foundational concept is the same, however, it's some place at home to be able to mess around with tools and technologies for the purposes of learning and fun. The great thing is that you don't have to have a lot of money to build a lot of infrastructure, you can easily use an old machine you have lying around.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can I do with a homelab?
&lt;/h2&gt;

&lt;p&gt;You can do a lot with a homelab. Anything you want that your hardware can handle, which is actually probably more than you think. Raspberry Pi's are surprisingly capable little devices, and even really old hardware can run a lot of open source software on Linux. There are so many common homelab projects and ideas you can find on the internet. A few include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up &lt;a href="https://pi-hole.net/" rel="noopener noreferrer"&gt;Pi-hole&lt;/a&gt; for network-wide adblocking&lt;/li&gt;
&lt;li&gt;Running a &lt;a href="https://www.plex.tv/" rel="noopener noreferrer"&gt;Plex&lt;/a&gt; media server to host your own media&lt;/li&gt;
&lt;li&gt;Set up a Raspberry Pi with &lt;a href="https://retropie.org.uk/" rel="noopener noreferrer"&gt;RetroPie&lt;/a&gt; for retro gaming&lt;/li&gt;
&lt;li&gt;Home automation with &lt;a href="https://www.home-assistant.io/" rel="noopener noreferrer"&gt;Home Assistant&lt;/a&gt; or &lt;a href="https://homebridge.io/" rel="noopener noreferrer"&gt;Homebridge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Custom code you write yourself to do whatever you'd like!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are so many other things you can do, that list barely scratches the surface of the possibilities. Just find something that sounds cool and get started. There are likely even tutorials that walk you through it!&lt;/p&gt;

&lt;h2&gt;
  
  
  My homelab hardware
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1sfogsdchm5xsdz2ukm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1sfogsdchm5xsdz2ukm.jpg" alt="My homelab" width="605" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my current homelab. I've been working on my homelab for a while, and this is by no means what I started out with. If you're interested in having a homelab, make sure you try your best to not constantly compare yours to others, because it's a slippery slope of feeling like you "need" more. (trust me, browsing &lt;a href="https://www.reddit.com/r/homelab" rel="noopener noreferrer"&gt;r/homelab&lt;/a&gt; often leaves me wanting to buy way more hardware, so I'm constantly needing to resist that urge) So with that out of the way, here's the hardware I have in my homelab server rack from top to bottom:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;StarTech 25U Open Frame Rack&lt;/li&gt;
&lt;li&gt;1U Brush Panel (makes things look nice with cables coming from the back)&lt;/li&gt;
&lt;li&gt;Ubiquiti Dream Machine Pro&lt;/li&gt;
&lt;li&gt;Ubiquiti Network Video Recorder&lt;/li&gt;
&lt;li&gt;24 Port Patch Panel&lt;/li&gt;
&lt;li&gt;Ubiquiti Switch 24 Port with Power over Ethernet&lt;/li&gt;
&lt;li&gt;24 Port Patch Panel&lt;/li&gt;
&lt;li&gt;Raspberry Pi 1U Rackmount with a Pi 3B+ and a Pi 4B+&lt;/li&gt;
&lt;li&gt;A few blanking panels &amp;amp; shelf holding my cable modem&lt;/li&gt;
&lt;li&gt;Dell PowerEdge R720 2U Rackmount server&lt;/li&gt;
&lt;li&gt;Synology DS920+ NAS&lt;/li&gt;
&lt;li&gt;StarTech Rackmount Power Strip&lt;/li&gt;
&lt;li&gt;Tripp Lite 300W 1U UPS&lt;/li&gt;
&lt;li&gt;Tripp Lite 900W 2U UPS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's go through the above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ubiquiti Networking
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqbfbkihuev4wu3ccn7e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqbfbkihuev4wu3ccn7e.jpg" alt="Ubiquiti" width="640" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of the Ubiquiti gear is the backbone of my home network. The UDM Pro is my router that provides firewall, VLANs, and more. I love this device and it was actually the first thing that started getting me interested in homelabbing. My Switch then connects all the rest of my hardwired devices such as the server, NAS, and my wireless access points and security cameras. That's why I have a PoE (power over ethernet) switch, because that's required for powering my APs and cameras. My Ubiquiti security cameras throughout my house record to the UNVR so I have all my recordings and data locally in my basement rather than in the cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Computing Hardware
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylbcull7zprir0x9s5d9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylbcull7zprir0x9s5d9.jpg" alt="Computing Hardware" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First I have my two Raspberry Pi devices. They're not currently plugged in since I moved their workloads onto my server recently, but once I have another need for them I'll plug them back in. I don't use their power cords because they each have a PoE "hat" which is a little add-on board that makes the Pi able to be powered over ethernet. Previously one was running Pi-hole, and the other was running Hoobs, a HomeBridge platform.&lt;/p&gt;

&lt;p&gt;I also have my Synology NAS (network attached storage) which has two 4 TB hard drives in it currently. (I'll be adding to it when I am getting close to the storage limit) This NAS hosts my media for my Plex server as well as a lot of backups of PCs and my server virtual machines.&lt;/p&gt;

&lt;p&gt;Finally I've got my Dell R720 server. I love this server, I bought it used on Ebay, and have loved using it ever since. It has 192 GB of RAM and a Xeon E5-2630 v2 CPU, and it's running Proxmox for virtualizing. It's got more than enough power to host the many services I've put on it so far, and there's plenty of room for more. I also have eight used 2 TB hard drives I bought on Ebay in this server which are used as the server local storage.&lt;/p&gt;

&lt;p&gt;These devices are definitely more than enough compute power for what I use currently. Eventually as I add more services I may outgrow my current hardware, and then I may acquire a little more hardware. The nice thing is the used market for these devices is thriving and can be a very affordable way to acquire this hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Services
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8l5m4261dydlh31gzvi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8l5m4261dydlh31gzvi.jpg" alt="Shipping Containers" width="640" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I run a variety of services in my homelab, almost all of it on my Dell server. Some I run as dedicated VMs, and others I run as docker containers in a Docker VM. There are so many ways to host these services, so I enjoy messing around with many of those methods. Here's a list in no particular order of the services I'm running in my homelab and a short description of what it's for.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pi-hole.net/" rel="noopener noreferrer"&gt;Pi-hole&lt;/a&gt;: Whole home adblocking&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hoobs.com/" rel="noopener noreferrer"&gt;Hoobs&lt;/a&gt;: HomeBridge which allows me to use non-HomeKit devices with Apple's HomeKit&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners" rel="noopener noreferrer"&gt;GitHub Actions Self-Hosted Runner&lt;/a&gt;: I use this to build and deploy my custom homelab code with GitHub Actions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/louislam/uptime-kuma" rel="noopener noreferrer"&gt;Uptime Kuma&lt;/a&gt;: A status page to monitor my homelab services&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/linuxserver/Heimdall" rel="noopener noreferrer"&gt;Heimdall&lt;/a&gt;: A homelab dashboard linking to all my services&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.plex.tv/" rel="noopener noreferrer"&gt;Plex&lt;/a&gt;: A self hosted media server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/portainer/portainer" rel="noopener noreferrer"&gt;Portainer&lt;/a&gt;: A nice UI to monitor and manage my docker containers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/traefik/traefik" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt;: Local reverse proxy which allows me to have SSL certificates for my self-hosted services&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pterodactyl.io/" rel="noopener noreferrer"&gt;Pterodactyl&lt;/a&gt;: A game server panel which allows me to spin up game servers for any game like Minecraft or Valheim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;: A tool to build dashboards with metrics and for searching logs across all my services&lt;/li&gt;
&lt;li&gt;Custom Code: I have a bunch of custom code I've written in the form of Slack bots, scripts, and other things that do whatever I want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fun thing about homelabs is you can run whatever you want to experiment with. While I currently run most of my stuff as Docker containers on a single Docker VM, I am planning to spin up a Kubernetes cluster and migrate my services to that so I can distribute the load across multiple nodes as opposed to throwing everything on one big Docker VM. That's just one item on my list, there are so many cool open source projects and technologies to mess around with, and it's as simple as just spinning up a VM or container for it and playing around. That's where the true fun of a homelab lies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is the state of my homelab at the beginning of 2023. Honestly I'm super excited to see where it'll be in a year or two. Maybe there's enough changes that I end up making this a yearly blog post to go over where my homelab is currently. I highly recommend that everyone who likes technology and messing around with software or hardware should start their own homelab. As I stated before, you can absolutely do a ton without spending a lot of money. Just use an old laptop or Raspberry Pi that's gathering dust. Use resources like &lt;a href="https://www.reddit.com/r/homelab/" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt; or YouTube channels like &lt;a href="https://www.youtube.com/@TechnoTim" rel="noopener noreferrer"&gt;TechnoTim&lt;/a&gt; or &lt;a href="https://www.youtube.com/@JeffGeerling" rel="noopener noreferrer"&gt;Jeff Geerling&lt;/a&gt; to get ideas, follow tutorials, or just enjoy their content. Or you can just mess around yourself and make a bunch of mistakes. It's a safe place to do that. And most of all just have a great time.&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>networking</category>
      <category>automation</category>
    </item>
    <item>
      <title>Angular CDK - Creating a Custom Dialog</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 28 Nov 2021 23:58:04 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/angular-cdk-creating-a-custom-dialog-m14</link>
      <guid>https://dev.to/johnbwoodruff/angular-cdk-creating-a-custom-dialog-m14</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the first in a series of posts about using the Angular CDK library to build your own custom components.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you're building applications using Angular, you probably know about the amazing &lt;a href="https://material.angular.io/"&gt;Angular Material&lt;/a&gt; library. It's a component library that implements the Material Design spec. For many teams and applications, particularly those without design resources, it's a fantastic tool for building applications with a high quality design system. I have used it many times with great success.&lt;/p&gt;

&lt;p&gt;There have been times, however, where I wanted the amazing developer experience of Angular Material, but I couldn't use the Material design that it implements because the company has an existing design system. It's precisely for these situations that the Angular team created &lt;a href="https://material.angular.io/cdk/categories"&gt;Angular CDK&lt;/a&gt;, or the "Component Dev Kit". According to their website, the CDK is "a set of behavior primitives for building UI components". The CDK is fantastic because it abstracts away a lot of the really complex behavior implementation in building UI components.&lt;/p&gt;

&lt;p&gt;Because of all these benefits, I try to always utilize the CDK for as much as possible when writing Angular applications. For this series of posts I hope to dig in to as many pieces of the CDK as possible to help you build your own high quality design system. In this post I'm going to specifically talk about building an alternative to &lt;code&gt;MatDialog&lt;/code&gt;, as that is a very commonly used service for creating modals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dialog Service
&lt;/h2&gt;

&lt;p&gt;In Angular Material, the &lt;code&gt;MatDialog&lt;/code&gt; service allows you to pass a component to the service which it will then open in a floating dialog that's globally centered both horizontally and vertically. Obviously this dialog implements the Material Design spec including animations when opening and closing. Because of this, we want to implement our own design, but the ergonomics of the &lt;code&gt;MatDialog&lt;/code&gt; service are great. So our implementation, while not exactly the same, will be similar and provide some of the same features.&lt;/p&gt;

&lt;p&gt;The design of the API is fairly simple, but extendable when needed. We'll have have an &lt;code&gt;open&lt;/code&gt; method that takes in an Angular component to be opened in the dialog. We can also pass data to the component that can be used if needed. This method will return a reference to the dialog that we can use to close it programatically or subscribe to when it's closed. This API design is simple and easy to extend as needed, but gets us a highly functional dialog service.&lt;/p&gt;

&lt;p&gt;Here is a demo of the finished product with all the features mentioned above. We'll be going through step by step but you're welcome to simply reference the code here if preferred.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/angular-ivy-sdmh7e?" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;First off we need to make sure we've got Angular CDK in our app. You have two ways you can do this. The first and least manual is to install it alongside Angular Material, via their provided Angular CLI schematic. Note that this will also install and configure Angular Material, a theme, and set up the styles. This is good in some ways because it includes the Angular CDK styles as part of the Angular Material theme. You can do that by using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ng add @angular/material
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you know for a fact that you're never going to want to use anything from Angular Material, and only want the CDK, then you can install it by itself from npm. Note that you must install the same version number that matches your Angular version, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @angular/cdk@12.2.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will not configure any styles, so you'll need to reference them properly as the docs outline for each piece you use. This is the method I'll be using in these tutorials because I know I won't want Angular Material as that is the whole point of this series. So now, no matter the path you chose, you have the Angular CDK installed and ready to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  CDK Overlay
&lt;/h2&gt;

&lt;p&gt;First let me explain how the CDK overlay concept works. There are three pieces to this that work together. There's the component we want to render, there's a &lt;a href="https://material.angular.io/cdk/portal/overview"&gt;Portal&lt;/a&gt; which is a CDK package for rendering dynamic content such as a component, and then there's an &lt;a href="https://material.angular.io/cdk/overlay/overview"&gt;Overlay&lt;/a&gt; which is a CDK package for opening floating panels on the screen. Basically what we do is attach a component to a &lt;code&gt;ComponentPortal&lt;/code&gt;, then attach that portal to an &lt;code&gt;OverlayRef&lt;/code&gt; which we'll open.&lt;/p&gt;

&lt;p&gt;There are a number of ways you can use the Angular CDK's overlay. You can use it programmatically or even as directives on markup. In our situation we want to use it programmatically so we can invoke it from anywhere via a service in our logic. So let's start out by creating a simple Angular service and stubbing out the basic API along with a couple of comments on what needs to be done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Create the overlay&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Attach component portal to the overlay&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the very beginning of our service. We know that we want an &lt;code&gt;open&lt;/code&gt; method, and we know it needs to take some component to open. You'll notice we're using the type of &lt;code&gt;ComponentType&lt;/code&gt; from the Angular CDK overlay package. This is a type that allows us to receive any Angular component, and that's what is passed to the CDK when instantiating the component. Of course we also have our generic &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; which will be the type of the component we pass through.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Overlay
&lt;/h3&gt;

&lt;p&gt;As we mentioned above we need to first create an overlay. To create an overlay we most importantly need a &lt;a href="https://material.angular.io/cdk/overlay/overview#position-strategies"&gt;PositionStrategy&lt;/a&gt;. This defines &lt;em&gt;where&lt;/em&gt; on the screen we want to open this overlay. There are a couple options, but in this post we'll be using the &lt;code&gt;GlobalPositionStrategy&lt;/code&gt;. This means we won't be attaching it to a specific element. We also can provide a few more optional configuration options, which we'll do. Here's how we create that overlay, injecting the &lt;code&gt;Overlay&lt;/code&gt; class in the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Globally centered position strategy&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;positionStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;centerHorizontally&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;centerVertically&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the overlay with customizable options&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;positionStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;hasBackdrop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;backdropClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overlay-backdrop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;panelClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;overlay-panel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach component portal to the overlay&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We did a couple of things. First, we defined our position strategy. We declared that we want a global strategy, and we want to position the overlay in the center of the screen both horizontally and vertically. You can also position your overlay (similar to absolute positioning) by giving it a top, left, right, or bottom value. This might be useful if you wanted to open a sidepanel or a bottom sheet. Since we're just making a standard modal, we're centering it on the screen.&lt;/p&gt;

&lt;p&gt;We're also defining some information about the panel and backdrop. First we're defining that we want a backdrop for this modal, as well as providing the backdrop class for that. That's where we can define how we want to style the backdrop, which I'll be styling with a darkly translucent backdrop. We're also providing a panel class, which will be applied to the parent "panel" that we'll be rendering our component in. I just did some basic styling to make the background white and have a little padding. You can see my styles I provided in &lt;code&gt;src/styles.scss&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Component Portal
&lt;/h3&gt;

&lt;p&gt;Next we need to create our &lt;code&gt;ComponentPortal&lt;/code&gt; that we'll then attach to the overlay. It's quite straightforward, and we do it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/portal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Globally centered position strategy&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the overlay with customizable options&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach component portal to the overlay&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;portal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we can successfully open this dialog if we actually call this method and pass a component to it. While we could just leave our service like this, it definitely doesn't meet the common usecases that we could do with &lt;code&gt;MatDialog&lt;/code&gt;. We want to be able return a dialog reference so we can programmatically close the overlay or subscribe to when the overlay gets closed. So let's add that to our implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dialog Reference
&lt;/h3&gt;

&lt;p&gt;Let's create a simple &lt;code&gt;DialogRef&lt;/code&gt; class. It should take in an &lt;code&gt;OverlayRef&lt;/code&gt; which we can use to close the overlay, and it should have an rxjs &lt;code&gt;Subject&lt;/code&gt; so we can subscribe to when the overlay is closed. So let's implement this simple class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OverlayRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * A reference to the dialog itself.
 * Can be injected into the component added to the overlay and then used to close itself.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;afterClosedSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OverlayRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Closes the overlay. You can optionally provide a result.
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;afterClosedSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;afterClosedSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * An Observable that notifies when the overlay has closed
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;afterClosed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;afterClosedSubject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asObservable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to add this in to our &lt;code&gt;open&lt;/code&gt; method so we can create this reference and return it from the method on creation. So let's put that in here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/portal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dialog-ref&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Globally centered position strategy&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the overlay with customizable options&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create dialogRef to return&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach component portal to the overlay&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is super helpful for the consumer of this API so they can access the dialog. But what about the component we're opening? We want to be able to allow the component in the overlay to close itself. So how can we pass that &lt;code&gt;dialogRef&lt;/code&gt; through? Well, for that we'll need to create an Injector which we pass to the component portal. This will allow us to then inject the &lt;code&gt;dialogRef&lt;/code&gt; in our component. It's pretty easy to do this, you can do it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/portal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dialog-ref&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Globally centered position strategy&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the overlay with customizable options&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlayRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create dialogRef to return&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create injector to be able to reference the DialogRef from within the component&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach component portal to the overlay&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;portal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've provided an injector to the component portal, we'll be able to inject the &lt;code&gt;dialogRef&lt;/code&gt; in our component very simply like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LoginComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dialogRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our implementation is much more thorough now that we have a way for the dialog to be programmatically closed from within the component or outside of it. The last major gap in functionality is being able to optionally pass some arbitrary data to the component that is being opened so it can utilize that data as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dialog Data
&lt;/h3&gt;

&lt;p&gt;In order to pass data to the component, we'll be using the same method as our &lt;code&gt;dialogRef&lt;/code&gt;. In this case, however, we'll need to &lt;a href="https://angular.io/guide/dependency-injection-in-action#injectiontoken-objects"&gt;define our own injection token&lt;/a&gt; for the dependency injection system. Let's start by doing that in a new file, &lt;code&gt;dialog-tokens.ts&lt;/code&gt;. It's going to be a very simple file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DIALOG_DATA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DIALOG_DATA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've created a very basic injection token, we can add this to our injector. We also need to update our &lt;code&gt;open&lt;/code&gt; method to accept optional data to be passed to the component. As part of that, we'll define a &lt;code&gt;DialogConfig&lt;/code&gt; interface that has optional &lt;code&gt;data&lt;/code&gt;. The reason we're making this a config object like this is so it's easy to extend later if you wanted to, for example, allow customizing the options for the overlay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentPortal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/portal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dialog-ref&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DialogConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DialogService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;DialogConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Globally centered position strategy&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the overlay with customizable options&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create dialogRef to return&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Create injector to be able to reference the DialogRef from within the component&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DialogRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DIALOG_DATA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach component portal to the overlay&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dialogRef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now that we've built this highly reusable service, we're able to open any component we want in a nicely centered modal! We can optionally provide data to it, and we can reference that dialog externally to close it if we wanted, or subscribe to it closing and react to that as needed. We could take this further, for example, by defining our own transitions and animations for the modal entering and exiting. Or we could easily pass in a config option to change the position strategy so it opens as a sidepanel instead of a centered modal. There are many ways you could tweak this to get exactly what you want, and it's fully within your control rather than being locked into the Material design dialog design and interactions.&lt;/p&gt;

&lt;p&gt;In my next post I'm going to go over the &lt;code&gt;FlexibleConnectedPositionStrategy&lt;/code&gt; that you can use to build things such as tooltips, popovers, dropdowns, and more. I'll be working hard on that one and hope to have it out soon. Hopefully this helps you get started digging into all the powerful options you have through the Angular CDK, and especially their Overlay package.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>cdk</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My Completely Biased Reasons for Choosing Angular</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Fri, 11 Jun 2021 20:14:48 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/my-completely-biased-reasons-for-choosing-angular-1hbg</link>
      <guid>https://dev.to/johnbwoodruff/my-completely-biased-reasons-for-choosing-angular-1hbg</guid>
      <description>&lt;p&gt;I wanted the title to be painfully obvious. Just in case that didn't make it obvious enough, let me be even more clear. &lt;strong&gt;This is my completely biased opinion.&lt;/strong&gt; You possibly vehemently disagree with me on this, &lt;em&gt;and that's okay.&lt;/em&gt; This is not a post to try to claim Angular is better than React or Vue or Svelt or whatever other framework you're in love with. It's literally a post talking about why I, John Woodruff, choose to use Angular in personal projects small and large. Honestly, I'm not even trying to convince you to use Angular. In fact, my honest advice for picking a framework for a project is to pick the one you know the best, so you can be as productive as possible. So let's get all that out of the way up front, and dive into my &lt;strong&gt;heavily biased&lt;/strong&gt; reasons for choosing Angular for personal projects. Keep in mind, when I make a statement it's an entirely opinion-based statement, so take it with a grain of salt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opinionated Framework
&lt;/h2&gt;

&lt;p&gt;Let's talk about one of the hottest topics up front. Angular is an opinionated framework. If you're not sure what that means, basically it's that the Angular framework defines how you should build applications, and they provide all of the essential tools you need to be able to build your applications. They provide solutions for routing, data fetching, internal data flow, and more, all bundled in the framework itself. Contrast this with something less opinionated like React which specifically does not define how you should build applications, it's simply a library to build components. You can then pick and choose any number of libraries for all the pieces you need to build your application, specifically the same things I mentioned above. &lt;/p&gt;

&lt;p&gt;So why is that a hot topic? Well, opinionated or less-opinionated frameworks or libraries elicit all sorts of responses from the developers who use them. Many developers are very against opinionated frameworks, where many other developers love opinionated frameworks. So naturally many of the arguments both in favor of and against Angular are based on the fact that it's a highly opinionated framework. They have rigid structure for how Angular apps should be built, and many tools included out of the box.&lt;/p&gt;

&lt;p&gt;Well here we come to my first of several biased opinions. I love Angular because it's an opinionated framework. I love that I don't have to pick and choose from a million libraries to put together a complex app. 95% of what I need is already included in the Angular framework. I also don't need to decide "how" I want to build my applications, because Angular has a detailed style guide for building applications, and I'm able to focus entirely on the actual implementation of my application.&lt;/p&gt;

&lt;p&gt;This is also why I love Angular for large complex apps within work environments. When working on teams there is often friction due to different teams or team members doing things differently. With Angular you eliminate a lot of that, because there are defined ways of doing things, and so it's far easier to scale across an organization. Having worked on large complex applications in work environments using both Angular and React, it's been infinitely easier to work within Angular applications due to the lack of a lot of the friction we had with the large React applications. It came down to Angular being opinionated, so there was far less mental overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular CLI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fma3ch7wrceuafrraz4nw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fma3ch7wrceuafrraz4nw.png" alt="Image of the terminal with an Angular CLI process running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ah the &lt;a href="https://angular.io/cli" rel="noopener noreferrer"&gt;Angular CLI&lt;/a&gt;. This goes right along with the previous point of Angular being opinionated. The Angular CLI is the best way to build Angular applications due to it tightly following the Angular style guide. It generates a fully scaffolded Angular project for you, and has numerous generator commands for adding new components, services, modules, etc., has automated testing all set up for you out of the box, and more.&lt;/p&gt;

&lt;p&gt;It also completely controls your build process, which means they fully manage the building and optimizing of your application. So all of your production builds make use of optimizations such as ahead-of-time compilation, source code minification, tree shaking, style auto-prefixing, and more. This is all stuff that you would have to figure out and do yourself using a build tool and numerous libraries and plugins. Instead of wasting time on all that, I can enjoy knowing that the Angular CLI is generating the best possible production build for me and I can focus on building awesome features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version Updates
&lt;/h2&gt;

&lt;p&gt;One of the best features of Angular CLI, if not the best feature, is the &lt;code&gt;ng update&lt;/code&gt; command. Ever since Angular 6 was released, the Angular CLI has included this command. It takes basically all the work out of doing version upgrades, and the Angular team did an absolutely phenomenal job of making this command work exceptionally well. They even have a super helpful &lt;a href="https://update.angular.io/" rel="noopener noreferrer"&gt;Update Guide&lt;/a&gt; which gives detailed instructions, but almost all of them say that the changes should be automated by the &lt;code&gt;ng update&lt;/code&gt; command. Normally when you have a major version update, you would have to manually go through your app updating dependencies, delving into changelogs, changing code in your app in numerous places to get rid of deprecated or removed features, and then painstakingly testing to make sure you haven't broken anything. This command, however, automates essentially all of that, including running code migrations that automatically migrate you to the latest recommended syntax. There have only been a handful of times where the changes required manual intervention in the code, and usually they were exceptionally quick to resolve. All the rest is fully automated by Angular CLI.&lt;/p&gt;

&lt;p&gt;Ever since this command was released, I have spent approximately 5-10 minutes updating to the latest each time a new major version is released. Contrast this with major version upgrades that can sometimes take hours or even days to update your large complex applications to the latest versions.  It even allows library authors to define their own schematics to automatically update their libraries, and that's awesome for users of the framework to not have to worry about manually keeping those up to date when they can just automate it. This has saved me countless hours every single time a major version is released, and I am completely spoiled when using other frameworks that don't provide this incredible functionality. (that's actually another upside to opinionated frameworks, it allows features like this that are otherwise unrealistic with unopinionated frameworks) The Angular team absolutely knocked it out of the park with this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular CDK
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kdgmqg64na4zple6td.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kdgmqg64na4zple6td.png" alt="Screenshot of the Angular CDK docs page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alongside &lt;a href="https://material.angular.io" rel="noopener noreferrer"&gt;Angular Material&lt;/a&gt; is a super awesome little package called &lt;a href="https://material.angular.io/cdk/categories" rel="noopener noreferrer"&gt;Angular CDK&lt;/a&gt;. CDK stands for Component Dev Kit, and it is an incredibly handy package for helping you develop some of the more complex components an application requires. They're marketed as "behavior primitives" that you can use to build your own branded components.&lt;/p&gt;

&lt;p&gt;Building buttons and input fields and such are fairly straightforward for people building component libraries, but there are other components that are much more complex such as modals, accordions, data tables, drag and drop, trees, and more. Rather than building all this yourself or relying on libraries that style these components how they want, Angular CDK gives you ways to very easily build your own complex behavioral components that you can style easily to fit your company or project's branding. Not only that, but these components are often much more accessible than components you would build yourself. As has been the theme with this post, Angular CDK helps you save a ton of time by having these abstractions built out for you so you can worry about the look, feel, and implementation of your components rather than the more complex details such as positioning, scroll behaviors, etc. It has saved me an enormous amount of time and complexity when building my components. If you're building with Angular, even if you don't use Angular Material, you should absolutely use Angular CDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Injection
&lt;/h2&gt;

&lt;p&gt;This is a hot topic for some reason, but Dependency Injection is another huge reason why I love to use Angular. It allows me to not have to worry about defining my own patterns for singleton vs factories. Instead, &lt;a href="https://angular.io/guide/dependency-injection" rel="noopener noreferrer"&gt;Angular's Dependency Injection&lt;/a&gt; tooling makes it exceptionally easy for me to provide the dependencies I need, anywhere I need them, and to do it in an easy manner. Rather than have to instantiate a service in a component, I can simply inject my service and Angular's Dependency Injection will ensure I am given the correctly instantiated service, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Some service I've defined&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Some component in my app&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other huge benefit to Dependency Injection is for better testability. Automated tests are something that I consider absolutely vital to the success or failure of a product and the team that builds it. Dependency Injection in Angular makes it incredibly easy to test, mock out, and handle dependencies external to the unit of code I'm testing at the moment. Consider the above component. To mock a method I simply need to inject the correct dependency and then utilize Jasmine's spies to mock out the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize Angular TestBed&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;compileComponents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Inject MyService for mocking&lt;/span&gt;
    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Mock out `sayHello` method&lt;/span&gt;
    &lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sayHello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;returnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It makes working in large complex codebases much more trivial, and makes testing vastly more simple. Are there downsides to Dependency Injection? Absolutely. No matter what pattern you choose, there are always going to be tradeoffs. It comes down to what tradeoffs you're willing to make in exchange for the benefits you consider most valuable. For me, Angular's Dependency Injection is one of the main reasons I choose it over other frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In case you've forgotten by this point, I'll reiterate one more time that this post is incredibly biased and entirely opinion-based. I absolutely love to use Angular, it's my framework of choice for side projects, and I believe it's an excellent choice for many of you as well. &lt;em&gt;That being said,&lt;/em&gt; I absolutely would argue that it's not a good choice for many others. When it comes down to it, you need to weigh the pros and cons of each framework, decide what tradeoffs you're willing to make, and choose based on what you decide. For many of you that's going to be React, or Vue, or Svelt, or Stencil, or Ember, or heck maybe even Backbone. And that's absolutely okay.&lt;/p&gt;

&lt;p&gt;I wanted to write this article to provide perspective to why I personally choose Angular over another framework. Not to provide more fodder for the "framework wars" or to bash on another person's choice. I will always say that the best framework choice for a project is the one you or your team is the most familiar with that will help you be the most productive and provide the fewest tradeoffs for what you want. In fact I love to read other peoples' completely biased articles on why they choose their framework (or library or text editor or whatever) and I enjoy celebrating their success and happiness over what they've chosen, while I enjoy what I've chosen. If there's anyone else out there like me who chooses Angular for their side projects, I'd love to chat in the comments about what your reasons are! And if you want to bash Angular or another framework for it not being as good as Framework X or Y, I humbly request you save those comments for the posts that encourage it. ❤🌈&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Combatting Burnout During COVID</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 04 Oct 2020 20:44:13 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/combatting-burnout-during-covid-1283</link>
      <guid>https://dev.to/johnbwoodruff/combatting-burnout-during-covid-1283</guid>
      <description>&lt;p&gt;I'll be completely honest, I'm feeling tired just thinking about writing this post. But that's not surprising, given the topic I'm writing about. Burnout. I've written about burnout before, whether it be comments on posts, or the chapter I wrote for &lt;a href="https://leanpub.com/firstyearincode" rel="noopener noreferrer"&gt;Your First Year in Code&lt;/a&gt;. I'm writing about it again because I'm currently feeling quite burned out. This, however, is a different kind of burnout than I've felt before. I've been burned out by too much coding, too little vacation, and other typical burnout reasons. This time there's no work-related reason for my burnout. This time, it's 2020 that is taking its toll on me, as I'm sure it's doing the same to many of you.&lt;/p&gt;

&lt;p&gt;COVID-19 has been bigger, longer, and crazier than I think most of us ever thought it would be. In the early days of COVID many thought it was a quick solution. Stay home for a few weeks, maybe a month or two, and it would fizzle out. Well, as history has shown us, it hasn't fizzled, it has exploded. Most tech companies have said they'll be full remote until January at least, at which point they'll "reevaluate", which likely means set another arbitrary date because there won't yet be a vaccine widely available. Many of us have been stuck inside without much social interaction since March. Even for the staunchest of introverts, that can be exceptionally taxing on one's mental health. I'm an extrovert and I feel exhausted not being able to fully charge that social battery.&lt;/p&gt;

&lt;p&gt;In short, I'm burned out. Not just from work, but from 2020. I also recognize that I have it better than many people. I've got a fantastic &lt;a href="https://dev.to/johnbwoodruff/my-home-office-workstation-981"&gt;home office setup&lt;/a&gt; which I feel productive in, and I've got a wonderful supportive wife and son, and work for a company that was very well prepared for remote work. Many people are in a far worse situation than me, so I recognize that my burnout may be nothing compared to what others are feeling. Nevertheless it's very real for me and I wanted to write about what I'm doing to attempt to combat it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hobbies
&lt;/h2&gt;

&lt;p&gt;I'm of the opinion that it's important to have hobbies outside of work. That being said I completely understand that many people enjoy programming as a hobby. (I'm definitely one of those people, and I've got a &lt;a href="https://dev.to/johnbwoodruff/rip-side-projects-2j34"&gt;side project graveyard&lt;/a&gt; to prove it) I have, in recent years, tried to find hobbies that aren't programming that I could indulge in. That has been super helpful during times of burnout, because when I get burned out it affects my hobby coding in addition to my professional coding.&lt;/p&gt;

&lt;p&gt;For myself personally I've had a variety of hobbies over the years. In addition to some of the typical ones (such as video games) I've enjoyed other hobbies that were less technical and more physical. One of my favorites is woodworking. My wife and I have built a variety of pieces I am proud of. My favorite was a dining table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuf2qvys0bh686opd9avd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuf2qvys0bh686opd9avd.jpg" alt="Handmade Dining Room Table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We had many family dinners around that table, and I felt very accomplished and greatly enjoyed working hard on this beautiful piece. It contributed greatly to me feeling fulfilled and definitely helped to hold off periods of burnout. There is something about working with physical objects and tools that breaks up the constant mental stress of working on a screen and with software. Perhaps something like that that would work for you to help fight off burnout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise
&lt;/h2&gt;

&lt;p&gt;This is one of the most commonly recommended things to help with burnout. (among many other ailments) I'm sure you, like me, are tired of hearing that exercise is good. It's one of those things where we all know it's something we're supposed to do, but despite that I personally tend to brush it off as something &lt;em&gt;other&lt;/em&gt; people need to do. It is, however, a very important part of keeping your mind and body healthy, something that directly affects your burnout.&lt;/p&gt;

&lt;p&gt;I've lately been in a better habit of exercise. Part of it is that I got a new puppy about 2 months ago! She's adorable, but she requires a lot of exercise to keep her healthy and happy, which means a lot of exercise for me. I take her on multiple walks every day to help her expend some energy, and she usually loves to run during the long stretches of grass, which I run with her.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk6lqrbh7epldiky1vl8n.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk6lqrbh7epldiky1vl8n.JPG" alt="Puppy outside in the backyard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another easy way to start getting into exercising is through VR if you're able to own something like an &lt;a href="https://www.oculus.com/quest-2/" rel="noopener noreferrer"&gt;Oculus Quest&lt;/a&gt;. While most games you don't move around much, there are plenty of games that can actually get your heart rate going decently well such as &lt;a href="https://beatsaber.com/" rel="noopener noreferrer"&gt;Beat Saber&lt;/a&gt;. On the Oculus Quest specifically there is a game called &lt;a href="https://www.oculus.com/experiences/quest/1830168170427369" rel="noopener noreferrer"&gt;Supernatural&lt;/a&gt; that is a fitness game. You pay for a membership and you get new workouts daily that tailor themselves to your fitness level. I tried it on a friend's headset before I got the Quest and it was a great workout. So there are plenty of ways to get exercise without needing to go to a gym or have lots of equipment at home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video Games
&lt;/h2&gt;

&lt;p&gt;I really enjoy video games as I'm sure many of you reading this article do as well. On the one hand playing video games keeps you staring at screens, so for some people it might not help with burnout. Also playing video games for huge periods of time can lend itself to not feeling great mentally. However I really enjoy playing video games for reasonable periods of time, especially with friends and family.&lt;/p&gt;

&lt;p&gt;Due to COVID most of us can't do close contact with friends and loved ones at the moment. I'm particularly close with my siblings, and pre-COVID we would get together regularly to hang out and enjoy each others' company. Since we can't do that right now, we decided to do a once-a-week video game night where we all play a video game together over voice chat. Our favorite to do is Age of Empires II, since that was one we played together frequently while growing up. It's really fun because we get to chat while playing, and we usually do a Zoom call afterwards to chat and reminisce.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx21fnwmtdq7gsjqddt5y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx21fnwmtdq7gsjqddt5y.jpg" alt="Age of Empires 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also enjoy &lt;a href="https://www.twitch.tv/jorgnaan" rel="noopener noreferrer"&gt;streaming on Twitch&lt;/a&gt; while I play video games. I'm by no means a streamer like many of you might be. I mostly stream just because it's fun when the occasional person hops on the stream and interacts with me. I only stream twice a week, usually to play Sea of Thieves on Tuesdays, and Rocket League on Fridays, with a variety of friends. Those are really fun to do. We play a couple hours and just have a fun time together, whether anybody watches or not. I definitely don't stream to do anything other than just have a good time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep in Touch with Friends &amp;amp; Family
&lt;/h3&gt;

&lt;p&gt;It's hard being apart from those you care about. That really starts to wear on you. So while we can't do much right now in terms of being together with groups of loved ones, we can find alternative ways to be together and enjoy each others' company. My family, for example, does a scheduled Zoom call once a week to stay updated with each others' lives. We share memories, talk about our week, and just have a nice time together.&lt;/p&gt;

&lt;p&gt;Another thing my family has started doing is sending weekly emails to each other on Sunday with photos and details of our lives and goings on during the week. It's been incredibly fun reading through what people are doing despite being restricted in what they normally do, and it's always enjoyable seeing photos of nieces and nephews and pets. In fact I've started saving those emails, and I'd love one day to compile a book of them for our family to look back on this crazy time and remember.&lt;/p&gt;

&lt;p&gt;If you live near family you can also do socially distanced visits. My parents live about 10 minutes from me so we like to go over every now and again and hang out in their backyard. We let our puppy run around and play, my son jumps on their trampoline, and my wife and I sit and chat with my parents, about 10 feet apart, on their back patio. While we can't give hugs or be physically closer than across the patio, it's really nice being able to be in somewhat close proximity with loved ones. I recognize that many people don't have the same luxury as I do of living close to family, but for those that do this can be incredibly helpful in trying to push away the burnout of being alone and separated from people, and we can still do it safely.&lt;/p&gt;




&lt;p&gt;Those are just a few of the things I've been doing to try to fend off the burnout that's been persistent during all the shutdown and restrictions of COVID. As with everything else in life this is by no means a one size fit all kind of thing. Obviously what works for me isn't necessarily going to work for you. I do hope, however, that by detailing some of the things that work for me, you'll at least find some ideas that work for you too. COVID has been rough for all of us in a million different ways, but frankly it's good to talk about when we're not feeling great. I'd love to hear what all of you are doing to combat the burnout many of us are feeling during COVID, maybe some of your ideas could work for me too.&lt;/p&gt;

</description>
      <category>career</category>
      <category>mentalhealth</category>
      <category>burnout</category>
    </item>
    <item>
      <title>Far More Epic Development Environment using WSL 2</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 16 Aug 2020 23:41:53 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/far-more-epic-development-environment-using-wsl-2-439g</link>
      <guid>https://dev.to/johnbwoodruff/far-more-epic-development-environment-using-wsl-2-439g</guid>
      <description>&lt;p&gt;By far my most read blog post was my first, &lt;a href="https://dev.to/johnbwoodruff/epic-development-environment-using-windows-subsystem-forlinux-5f0n"&gt;Epic Development Environment using Windows Subsystem for Linux&lt;/a&gt;, and to this day it gets many views here on DEV, as well as on Medium where I originally published it, as people search for WSL to find out if it'll work for them. Well, since I published it at the end of 2017, a lot has changed, and I'm not referring to the pandemic in which we all find ourselves. I'm talking about WSL 2.&lt;/p&gt;

&lt;p&gt;This year Microsoft released Windows Subsystem for Linux 2 alongside the Windows 10 version 2004 update. There are a number of differences between version 1 and 2. You can read more about the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/compare-versions"&gt;exact differences&lt;/a&gt; but the key takeaways are that WSL 2 offers 100% system call compatibility, much faster performance, and less memory usage. Note that you only get the fast performance if you store the files you work with in the Linux filesystem. If for some reason you need to store your files in the Windows filesystem, you'll get better performance using WSL 1.&lt;/p&gt;

&lt;p&gt;Since I've upgraded my setup to use WSL 2, my quality of life has been upgraded in a major way. There have also been other advancements in a variety of other areas that make my new setup so much better than what I wrote about in my previous article. I'm super excited to go over them with you all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To use WSL 2 you must be running Windows 10 version 2004 or greater. WSL 2 is not available in builds before version 2004. In my previous article you needed Windows 10 Pro so Docker could use Hyper-V virtualization. Thankfully that's no longer required!&lt;/p&gt;

&lt;h2&gt;
  
  
  Install WSL 2
&lt;/h2&gt;

&lt;p&gt;Let's get WSL 2 installed. The first thing you need to do is enable the Windows Subsystem for Linux feature in Windows. You can do this simply by running the following command in PowerShell as an Administrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will enable the WSL feature. At this point you'll want to follow the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2"&gt;Update to WSL 2&lt;/a&gt; instructions to update the WSL 1 installation to WSL 2. In short you need to run the following command, then restart your computer to complete installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your computer has been restarted, make sure you run the following command as Administrator in PowerShell to ensure WSL 2 is always the default WSL version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="nt"&gt;--set-default-version&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; You might get a message saying something like "WSL 2 requires an update to its kernel component". Make sure you follow the link provided and do what's needed.&lt;/p&gt;

&lt;p&gt;At this point you can go to the Microsoft Store app and search for your favorite Linux distribution! Please note that this article assumes you are using Ubuntu. You can use any distro, however, you will simply need to find the correct commands to install things. Once you've installed your distro, go ahead and open it to perform the initial installation. It will ask you for a user and password for the Ubuntu userspace. When it's done, you're ready to get to installing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Git
&lt;/h2&gt;

&lt;p&gt;As before, many of the installations further on in this article require git. To install, simply run &lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install git&lt;/code&gt;. You should also generate an SSH key. This is very simply accomplished by running: &lt;code&gt;ssh-keygen -t rsa -b 4096 -C "your_email@example.com"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ZSH (Optional)
&lt;/h2&gt;

&lt;p&gt;I'm simply going to copy paste this section from my previous article as nothing has changed at all here, and I'm still digging my ZSH setup.&lt;/p&gt;

&lt;p&gt;This next step is completely optional. I love bash, and I love zsh, so either one works fine. &lt;strong&gt;If you prefer bash, skip this section.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s fairly straightforward to install zsh. After making sure you’ve run &lt;code&gt;sudo apt update&lt;/code&gt; then install it with &lt;code&gt;sudo apt install zsh&lt;/code&gt;. You can test to make sure it’s working by running &lt;code&gt;zsh&lt;/code&gt; which should bring you into a zsh terminal!&lt;/p&gt;

&lt;p&gt;I also love &lt;a href="https://github.com/robbyrussell/oh-my-zsh"&gt;oh-my-zsh&lt;/a&gt;, which provides lots of beautiful themes and excellent plugins. I used the &lt;code&gt;curl&lt;/code&gt; command under the &lt;a href="https://github.com/robbyrussell/oh-my-zsh#basic-installation"&gt;Basic Installation&lt;/a&gt; instructions, and I was good to go! You can then select the theme you want by editing your &lt;code&gt;~/.zshrc&lt;/code&gt; file and adding the theme name to the &lt;code&gt;ZSH_THEME&lt;/code&gt; environment variable. Previously I was using the Pure theme for ZSH, but I've since moved over to the venerable &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;Powerlevel10k&lt;/a&gt; theme. It's super customizable, easy to setup, and has excellent speed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S5R7eEZn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/pq2uvxlg5maarilm36dh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S5R7eEZn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/pq2uvxlg5maarilm36dh.png" alt="Powerlevel10k Theme" width="765" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;Featured image from the &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;Powerlevel10k&lt;/a&gt; repository&lt;/small&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Windows Terminal
&lt;/h2&gt;

&lt;p&gt;This is one area where I've absolutely changed my mind from 2017. Back then I used the Hyper terminal by Zeit. (The company has since rebranded to Vercel) While I still use that on my Mac, on Windows I now use the phenomenal &lt;a href="https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701"&gt;Windows Terminal&lt;/a&gt;. It's fast, modern, and beautifully designed. It also works seamlessly with WSL, PowerShell, CMD, and more! It's got multiple tab support, Unicode support, custom themes, and a bunch of other great features. I highly recommend it for anyone doing any kind of development on Windows, whether it's WSL or not. You can simply find it on the Microsoft Store and install it from there for free.&lt;/p&gt;

&lt;p&gt;When you launch Windows Terminal &lt;em&gt;after&lt;/em&gt; having installed and run Ubuntu for the first time, it should pick up the distro automatically and add it to your terminal settings so it's an option to use. I like to make it my default when I open Windows Terminal, as well as a couple other changes to the configuration. Here's my specific Windows Terminal config for my Ubuntu distro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{GUID_HERE}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hidden"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Windows.Terminal.Wsl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startingDirectory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"//wsl$/Ubuntu/home/{UBUNTU_USERNAME}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fontFace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fira Code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"useAcrylic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acrylicOpacity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set a few things here. First of all, I set my &lt;code&gt;startingDirectory&lt;/code&gt; to be my Ubuntu home directory. Simply replace &lt;code&gt;{UBUNTU_USERNAME}&lt;/code&gt; with your actual ubuntu username. I also &lt;strong&gt;love&lt;/strong&gt; the &lt;a href="https://github.com/tonsky/FiraCode"&gt;Fira Code&lt;/a&gt; font, so I've got that installed on my machine and I have my terminal using it. Lastly, I'm a huge fan of the Windows acrylic look, so I have my terminal using that effect. You're welcome to set that to &lt;code&gt;false&lt;/code&gt; if you don't like it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sznE3S4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/z9irmrr5on3wwd4xqyks.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sznE3S4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/z9irmrr5on3wwd4xqyks.jpeg" alt="Windows Terminal" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;Screenshot from the Windows Terminal store page&lt;/small&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Visual Studio Code
&lt;/h2&gt;

&lt;p&gt;What has not changed is that &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; is possibly my favorite piece of software ever. As mentioned in my previous article, I’ve used IDEs, Sublime Text, Atom, Notepad++, I’ve tried almost all of the most used ones. VS Code is by far my favorite editor. It’s beautiful, incredibly lightweight, super fast, has tons of amazing extensions, built in debugger support, and is as close to full IDE functionality as any text editor I’ve ever used. I installed this as quickly as I could and brought down all &lt;a href="https://gist.github.com/jbw91/e77025c1fc4fe1d5137ac525398f2227"&gt;my settings&lt;/a&gt; using the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync"&gt;Settings Sync extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; As of a couple days ago I probably won't be using the Settings Sync extension anymore as VS Code now has &lt;a href="https://code.visualstudio.com/docs/editor/settings-sync"&gt;built in settings sync support&lt;/a&gt;. My initial tests have been very positive.&lt;/p&gt;

&lt;p&gt;There's one more extension you must get if you're doing WSL 2 development. This is different from my last article as this extension did not exist at the time. That's the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl"&gt;Remote - WSL&lt;/a&gt; extension. This little extension makes many of the little gotchas I previously experienced go away. I used to have to install a tool called wsl-git that would proxy Windows git requests to WSL. This was so I could get the git features in VS Code to work. With this WSL extension you no longer need any of that. When you run &lt;code&gt;code .&lt;/code&gt; from a folder inside of your WSL terminal, it will open that folder in VS Code and automatically use all the features and binaries from your WSL 2 installation instead of from Windows. You can install VS Code extensions that use the Linux binaries. It's a totally seamless experience that makes you forget you're not using Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y5AwRNos--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/uhk8pa1bq0vqfvws2m52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y5AwRNos--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/uhk8pa1bq0vqfvws2m52.png" alt="Visual Studio Code" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;My Visual Studio Code setup with the Remote - WSL extension&lt;/small&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Node.js/NPM
&lt;/h2&gt;

&lt;p&gt;This section remains the same as my previous article. Once again copying it over for posterity.&lt;/p&gt;

&lt;p&gt;Personally I decided to take the approach of doing as much as absolutely possible inside of the WSL environment. Also, I am obsessed with NVM for managing my Node installs, which doesn’t work on Windows. So, instead of downloading the installer for Node.js for Windows, I installed nvm through Ubuntu’s repositories! You can follow the directions on &lt;a href="https://github.com/creationix/nvm#install-script"&gt;NVM’s Installation Instructions&lt;/a&gt;. I used &lt;code&gt;curl&lt;/code&gt; to download the install script, which I installed by running &lt;code&gt;sudo apt install curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you did everything right, you should have nvm installed correctly. I then installed the Long Term Support version of node by running &lt;code&gt;nvm install --lts&lt;/code&gt; and then &lt;code&gt;nvm use --lts&lt;/code&gt; to set it as the version I’m currently using. I also set it to my default node version by running &lt;code&gt;nvm alias default {VERSION}&lt;/code&gt; where VERSION is the version number you just installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--700NHWzY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/m8xljjvspefuwsxadmco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--700NHWzY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/m8xljjvspefuwsxadmco.png" alt="Alt Text" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;A global npm install of the Angular CLI &amp;amp; Typescript npm packages&lt;/small&gt;&lt;/center&gt;

&lt;h3&gt;
  
  
  Yarn
&lt;/h3&gt;

&lt;p&gt;As an aside to installing Node, I thought I’d bring up that I also installed &lt;a href="https://yarnpkg.com/"&gt;Yarn&lt;/a&gt;, because I love Yarn. You can install it through Windows, but again, I’m trying to do everything I need through WSL, so I followed the &lt;a href="https://yarnpkg.com/en/docs/install#linux-tab"&gt;Linux Installation Instructions&lt;/a&gt;. Specifically the Ubuntu/Debian instructions, of course. This went without a hitch, and I had yarn working!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c4bV4yc8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/z7c93enbma2lc1lvzxyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c4bV4yc8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/z7c93enbma2lc1lvzxyf.png" alt="Yarn install" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;A yarn install working beautifully&lt;/small&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;This is one of the best parts of WSL 2 that has changed since last time. &lt;a href="https://docs.docker.com/docker-for-windows/install/"&gt;Docker for Windows&lt;/a&gt; has been updated to support using WSL 2 as the backend for Docker. This was one of the pain points in the setup in WSL 1, and I had to alias the Windows executables to bash commands. There were occasional gotchas that generally were annoying. This is fantastic news, as those gotchas are all gone, and it's a totally seamless experience!&lt;/p&gt;

&lt;p&gt;To get Docker up and running, simply download &lt;a href="https://www.docker.com/products/docker-desktop"&gt;Docker Desktop&lt;/a&gt;. During the installation it should prompt you whether or not you'd like to use the WSL 2 integration, so make sure you enable that. The &lt;a href="https://docs.docker.com/docker-for-windows/wsl/"&gt;Docker Desktop WSL 2 Backend&lt;/a&gt; documentation gives detailed instructions on how to make sure things work properly in case you're having troubles getting this working.&lt;/p&gt;

&lt;p&gt;At this point you should be able to use Docker and Docker Compose beautifully inside of both your Windows PowerShell environment as well as inside of your default WSL distro! (you can enable it inside of other WSL distros besides your default one in the WSL Integration settings in Docker Desktop)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BJLcKKOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/2orcwtipqsz1ogpti7fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJLcKKOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/2orcwtipqsz1ogpti7fw.png" alt="Docker running mysql" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;&lt;small&gt;Docker for Windows pulling and running the mysql container&lt;/small&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Last Words
&lt;/h2&gt;

&lt;p&gt;I'm hugely impressed with everything the WSL team has accomplished. WSL 2 is a massive improvement over WSL 1 and alongside other tools from Microsoft that integrate seamlessly with WSL 2, it's a phenomenal development environment that has very few if any compromises for the web development work I do. I have a work-issued Macbook Pro that I use through the work day, but I now immediately switch to my Windows machine when the work day is over and all the rest of what I do is done there with great enjoyment.&lt;/p&gt;

&lt;p&gt;Four years ago I had totally written off Windows for development. For me it was either Mac or dual boot a Linux distribution. I would occasionally come back and try Windows for coding, and would almost immediately give up. WSL 1 changed that for my personal side projects. To be honest, however, WSL 2 has me fully believing I could use Windows exclusively for everything I do and be super happy doing it. That's an awesome feeling for someone who has always loved Windows as a consumer operating system, and I'm so excited to see what other amazing dev tools come to Windows because of the amazing tool of WSL 2.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wsl2</category>
      <category>setup</category>
      <category>windows</category>
    </item>
    <item>
      <title>My Home Office &amp; Workstation</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Thu, 24 Oct 2019 23:53:01 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/my-home-office-workstation-981</link>
      <guid>https://dev.to/johnbwoodruff/my-home-office-workstation-981</guid>
      <description>&lt;p&gt;This year I got a new job at a place called &lt;a href="https://get.goreact.com/careers/" rel="noopener noreferrer"&gt;GoReact&lt;/a&gt; that allows me to work remote if I wish. I currently am enjoying working remote 3 times a week, and going in to the office on the other 2 days. As such, I needed a comfortable, ergonomic, productive workspace for myself at home. I have the fortune of living in an apartment that has an extra bedroom we don't use, so I use that as my home office. It's taken me close to a year to acquire all the pieces I wanted in a dream office, but I finally have a space put together that helps me feel inspired and productive while working. So, without further ado, here it is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjluqf43q0sb3rfijjuj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjluqf43q0sb3rfijjuj7.png" alt="Side view of my workstation setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's another view of it from straight on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo1ay93hvy3nzvnte4v0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo1ay93hvy3nzvnte4v0n.png" alt="Front view of my workstation setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm very proud of this workspace. Sure, it could use some help in the cable management department, among other places, but I love it. A lot of this I owned before setting up this home office, and some of it I bought when getting my space set up.&lt;/p&gt;

&lt;p&gt;Here is a list of the things you'll see that make up my workspace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.autonomous.ai/standing-desks/smartdesk-2-business" rel="noopener noreferrer"&gt;Autonomous SmartDesk Business Edition&lt;/a&gt; with Cable Tray (to help with cord management)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.autonomous.ai/office-chairs/ergonomic-chair" rel="noopener noreferrer"&gt;Autonomous ErgoChair 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.autonomous.ai/office-accessories/anti-fatigue-mat" rel="noopener noreferrer"&gt;Anti-Fatigue Mat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apple.com/macbook-pro/" rel="noopener noreferrer"&gt;15" MacBook Pro&lt;/a&gt; (work issued)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/gp/product/B072C7TNC5/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Samsung CHG90&lt;/a&gt; Series Curved 49" monitor&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B01N5UOYC4/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Logitech BRIO Ultra HD Webcam&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codekeyboards.com/" rel="noopener noreferrer"&gt;CODE Mechanical Keyboard&lt;/a&gt; with Cherry MX Clear switches&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/shop/product/MLA02LL/A/magic-mouse-2-silver" rel="noopener noreferrer"&gt;Apple Magic Mouse 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/shop/product/MRMF2/magic-trackpad-2-space-gray?fnode=4c" rel="noopener noreferrer"&gt;Apple Magic Trackpad 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/Bose-QuietComfort-Acoustic-Cancelling-Headphones/dp/B00M1NEUKK" rel="noopener noreferrer"&gt;Bose QuietComfort 25&lt;/a&gt; Noise Cancelling Headphones&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/gp/product/B07BHP4W36/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Anker Soundcore&lt;/a&gt; Bluetooth Speaker&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B0756Z8X82/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Anker Wireless Charger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/gp/product/B07238CZ2H/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Pictostone Notebook&lt;/a&gt; - Stone Paper Notebook&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/gp/product/B0711ZWHDV/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;ORICO USB 3.0 Hub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/gp/product/B012VIWG28/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;Brainwavz Hengja&lt;/a&gt; - Desk Headphone Hanger Mount&lt;/li&gt;
&lt;li&gt;Various dongles because USB-C 😅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Miscellaneous non-work related items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dwight Schrute Bobblehead&lt;/li&gt;
&lt;li&gt;Dundie Award&lt;/li&gt;
&lt;li&gt;Korbanth Graflex 2.5 Lightsaber (empty hilt kit, self-assembled)&lt;/li&gt;
&lt;li&gt;DJI Ronin S&lt;/li&gt;
&lt;li&gt;Breath of the Wild Divine Beasts desk light from &lt;a href="https://www.etsy.com/shop/VividDelights" rel="noopener noreferrer"&gt;VividDelights&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, that's it. That's my workspace. It's got two windows so I get plenty of natural light. (they were closed so the photos wouldn't be backlit like crazy) It has a door I can close and lock so I don't need to worry about my sweet 5 year old walking in while I'm in an important meeting. It works perfectly for me.&lt;/p&gt;

&lt;p&gt;I'm quite satisfied with the current state of my office. I am, however, saving up for a custom build gaming PC. My MacBook Pro is work-issued, can't play most of my video games, and definitely can't do VR. (I have an Oculus Rift S) So it may be close to a year before I can get that, but that's next on my list of what to add once I've saved up a bit more.&lt;/p&gt;

&lt;p&gt;Let me know what you think! I'd also love to see some other workstations from the rest of y'all!&lt;/p&gt;

</description>
      <category>workstations</category>
      <category>remote</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Fighting About JavaScript Frameworks</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Wed, 21 Nov 2018 06:17:46 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/stop-fighting-about-javascript-frameworks-2307</link>
      <guid>https://dev.to/johnbwoodruff/stop-fighting-about-javascript-frameworks-2307</guid>
      <description>&lt;p&gt;The &lt;a href="https://stateofjs.com/" rel="noopener noreferrer"&gt;State of JS&lt;/a&gt; survey is live, and with it comes the usual tweets about this or that framework "dominating" others, one or another "has been destroyed", it goes on and on. Even the &lt;a href="https://2018.stateofjs.com/front-end-frameworks/overview/" rel="noopener noreferrer"&gt;survey itself says&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The front-end remains the key battleground for JavaScript. But now that the dust has cleared, it's starting to look like only two combatants are left standing...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's disappointing to say the least that the JavaScript community that I love so much has turned into one giant argument about who's better, cooler, in other words, the "winner".&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3l22yyulrhq1n6nkrdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3l22yyulrhq1n6nkrdl.png" alt="React vs Angular vs Vue" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  React vs Angular vs Vue vs...
&lt;/h2&gt;

&lt;p&gt;This is the tale as old as time. I can't tell you how many articles I've seen and read about why React is better, Angular is better, Vue is better, Vanilla JS is better, etc. There is the full spectrum of these posts. From being very fair and open minded by addressing the best and worst of each framework and inviting the user to decide what works for them, to being downright biased and essentially saying Framework X sucks and Framework Y is the best, with little offered for reasoning.&lt;/p&gt;

&lt;p&gt;All this serves to do is foster a culture of superiority and gatekeeping. You're not a real developer because you use the inferior framework. Unless you're using the hottest framework, you're not worth hiring at my hip and happening startup.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzitf8oauoi17pb7shhf7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzitf8oauoi17pb7shhf7.png" alt="Developers" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It Hurts Everyone
&lt;/h2&gt;

&lt;p&gt;Saying and promoting ideas like this is damaging to the community as a whole. Take the State of JS survey for example: The number of respondents was approximately 20,000 developers. That's very small representation compared to the industry. Stack Overflow's survey had over 100,000 respondents.&lt;/p&gt;

&lt;p&gt;There is also something to be said about the fact that the makers of the survey are React library authors with heavy React developer followings. So naturally simply due to the audience they cater to, there will be a hefty amount of React developers responding to and sharing the survey amongst themselves.&lt;/p&gt;

&lt;p&gt;The problem with this is that this survey is being presented as a definitive view of JavaScript in 2018. With such a limited and focused sample size, the results are likely skewed towards certain groups. This is problematic for developers who take those results and show them to their boss saying "look at these results, Angular is dead, we need to migrate all of our code to React or Vue". I've literally had discussions with people to that effect. It's not based on what the &lt;em&gt;best&lt;/em&gt; option is, but rather the most popular according to one small survey or blog claiming to be the definitive view of the JavaScript world.&lt;/p&gt;

&lt;p&gt;This is harmful to everyone, because companies and developers will spend a lot of time doing what someone views as the most popular thing to do rather than the option that might be best for the company. For example, rewriting an app from Angular.js to React despite the fact that an Angular.js to Angular transition may be faster and easier due to the concepts and fundamentals being similar. I've also heard of a developer being hired at a startup and telling them that Angular is the best framework and they need to rewrite their entire React app in Angular, costing months in time and money.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwx8dksm8yldwrbrq5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwx8dksm8yldwrbrq5k.png" alt="Holding Hands" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop the Hot Takes
&lt;/h2&gt;

&lt;p&gt;I'm personally over all the pundits proclaiming why one framework is better than another, or declaring a winner of the "framework war". It is, in my opinion, a form of gatekeeping that we as a community don't need. Why does there have to be a war? I would argue that there's not, or at least shouldn't be, a war. Especially in 2018, when React, Angular, Vue, Aurelia, etc. are all excellent modern frameworks with amazing reasons to use each.&lt;/p&gt;

&lt;p&gt;Use what makes you happy. If it's React, Angular, Vue, something completely different, if it is enjoyable and helps you to be more productive, then it's a good thing. Let's please stop using these surveys and articles to try to convince everyone else that your choice is right and theirs is wrong. Let's love each other and love our own choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I am a happy user of both Angular and React. This article is not intended to be a low-key "Why Didn't Angular Get First Place". Hopefully that's not how it sounds. I simply want to help end the "war".&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>frameworks</category>
      <category>stateofjs</category>
    </item>
    <item>
      <title>Changelog: New StackBlitz Liquid Tag</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Mon, 19 Nov 2018 17:44:59 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/changelog-new-stackblitz-liquid-tag-187e</link>
      <guid>https://dev.to/johnbwoodruff/changelog-new-stackblitz-liquid-tag-187e</guid>
      <description>&lt;p&gt;&lt;em&gt;A new challenger approaches!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Joining the likes of the various other embedded code environments such as CodeSandbox, Codepen, and Glitch, there is a shiny new StackBlitz Liquid Tag! &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;, the online IDE for web applications powered by Visual Studio Code, has become the go-to online editor solution for many developers, many of whom even use StackBlitz to power their documentation examples. (such as the Angular team)&lt;/p&gt;

&lt;p&gt;To use the liquid tag, simply add your project ID to the liquid tag. This may be the auto-generated ID when you created the project, or whatever you renamed your project to. Like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{% stackblitz ball-demo %}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/ball-demo?" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There you have it! Enjoy the awesomeness that StackBlitz can bring to your blog posts! 🎉&lt;/p&gt;

</description>
      <category>meta</category>
      <category>changelog</category>
      <category>stackblitz</category>
    </item>
    <item>
      <title>Component Libraries with Stencil.js - Decorators</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Sun, 18 Nov 2018 23:40:19 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---decorators-1b9c</link>
      <guid>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---decorators-1b9c</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the fifth in a series of posts about creating a web component library using Stencil.js - Check out the &lt;a href="https://dev.to/johnwoodruff91/component-libraries-with-stenciljs---about-stencil-10b7"&gt;first post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At this point we've built a fairly basic component that looks great and functions correctly. We're now going to move on to a new component that has more complex functionality and interaction. Tabs are one of the basic components every library needs. Each tab is effectively a button, but those buttons work together and maintain a shared state with regards to which tab is currently active. This is a perfect candidate for our next component, as it can use all the rest of the &lt;a href="https://stenciljs.com/docs/decorators#decorators" rel="noopener noreferrer"&gt;decorators&lt;/a&gt; Stencil provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Blocks
&lt;/h2&gt;

&lt;p&gt;Let's first create an incredibly simple component with no styling or functionality. Start by creating a new folder, &lt;code&gt;tabs&lt;/code&gt;, which contains three files: &lt;code&gt;tabs.tsx&lt;/code&gt;, &lt;code&gt;tab.tsx&lt;/code&gt;, and &lt;code&gt;tabs.scss&lt;/code&gt; for our styles. Let's start with the building block tab component.&lt;/p&gt;

&lt;p&gt;This will be a little different. It will be a component, but will not have a render function. It will instead keep track of a couple of props for us. Add the following to your &lt;code&gt;tab.tsx&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mtn-tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tab&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reflectToAttr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've got props for the label, which the tab will display, a flag that shows whether or not it's active, and of course a disabled property. If we wanted to keep more metadata associated with each individual tab, this is where we'd add it.&lt;/p&gt;

&lt;p&gt;Now let's dig into our main tabs component in &lt;code&gt;tabs.tsx&lt;/code&gt;. We first need to render the basic structure of the component. Let's start with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCssClassMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classMap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Tab 1
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Tab 2
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Tab 3
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getCssClassMap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;CssClassMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tabs-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also get some very basic styling in place to our host component and &lt;code&gt;.tabs-list&lt;/code&gt; class. In our &lt;code&gt;tabs.scss&lt;/code&gt; file we'll put in a few blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'../../styles/variables.scss'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.tabs-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#dddddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7bao5gq2oe5ba335zbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7bao5gq2oe5ba335zbf.png" alt="ugly tabs" width="734" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This obviously looks really ugly and not at all like tabs, but it's a start, and we simply need to style them to be a little nicer. Let's add a few simple styles to make them look beautiful.&lt;/p&gt;

&lt;p&gt;Let's give each button a &lt;code&gt;.tab-button&lt;/code&gt; class and an &lt;code&gt;.active&lt;/code&gt; class to the first one. Now for the class definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tab-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$font-family&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.active&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nv"&gt;$light-dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nv"&gt;$blue-steel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're doing a couple things here that you can work out, but basically we're removing the styling that buttons have, tweaking the font, and then adding a nice border at the bottom of the tab when it's active. We're also doing a fun little hover border with a light gray color when hovering over a tab that's neither disabled nor currently active. We're of course also defining what a disabled tab looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcshr0m779cfmd835dzca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcshr0m779cfmd835dzca.png" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our tabs look like actual tabs, but they're also hardcoded. We want to be able to specify how many tabs we want, as well as customize their labels. Here's where we get into the other decorators.&lt;/p&gt;

&lt;h2&gt;
  
  
  State and Element Decorators
&lt;/h2&gt;

&lt;p&gt;First let's define the tabs and dynamically create them. These tabs define how we render our component, so we're going to define them along with the &lt;code&gt;@State()&lt;/code&gt; decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;State&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLMtnTabElement&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@State()&lt;/code&gt; decorator is used to manage data that is internal to the component. Any changes to this property will cause the component to re-render, as the state has changed.&lt;/p&gt;

&lt;p&gt;You'll also notice I'm using the &lt;code&gt;HTMLMtnTabElement[]&lt;/code&gt; type for the tabs. This is an interface that is created automatically by Stencil in your &lt;code&gt;components.d.ts&lt;/code&gt; file. It gives you some nice intellisense for your component, with the standard HTML element properties and methods, as well as those you define on your component.&lt;/p&gt;

&lt;p&gt;With our tabs, we want to use the following syntax to define them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;mtn-tabs&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mtn-tab&lt;/span&gt; &lt;span class="na"&gt;active&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Tab 1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/mtn-tab&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mtn-tab&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Tab 2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/mtn-tab&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mtn-tab&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Tab 3"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/mtn-tab&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mtn-tab&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Tab 4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/mtn-tab&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/mtn-tabs&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use the &lt;code&gt;componentWillLoad()&lt;/code&gt; lifecycle hook to grab the tabs we've specified and render them. We're also going to have to use the &lt;code&gt;@Element()&lt;/code&gt; decorator so we can reference it in our method.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@Element()&lt;/code&gt; method is pretty simple. It returns an instance of the host &lt;code&gt;HTMLElement&lt;/code&gt; of your component. We want to query elements within our own component, so we're going to use that in our &lt;code&gt;componentWillLoad()&lt;/code&gt; method like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Our host element&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;State&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLMtnTabElement&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;componentWillLoad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Grab tabs from this component&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mtn-tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[mtn-tabs] Must have at least one tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This snippet grabs all &lt;code&gt;mtn-tab&lt;/code&gt; elements and sticks them in our &lt;code&gt;tabs&lt;/code&gt; property. Let's then adjust our &lt;code&gt;render()&lt;/code&gt; function to use the dynamically grabbed tabs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCssClassMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classMap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLMtnTabElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;tabClassMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CssClassMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;

          &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
              &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt;
              &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tabClassMap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever used React, you'll recognize what we're doing here. Remember that TSX is TypeScript, not actual HTML, so we can do things such as transform arrays into other rendered elements. We're doing this in our function here. We're taking &lt;code&gt;this.tabs&lt;/code&gt; and using the &lt;code&gt;map()&lt;/code&gt; function to transform those tabs into elements we want to render on screen. In this case we're rendering our buttons with the &lt;code&gt;.tab-button&lt;/code&gt; class, and we're conditionally applying the &lt;code&gt;.active&lt;/code&gt; class to the tab which is marked as active. We make sure to assign the disabled attribute conditionally to those tabs that we've marked as disabled. We also specified a click handler, &lt;code&gt;onClick&lt;/code&gt;, that we'll implement now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method Decorator
&lt;/h2&gt;

&lt;p&gt;We want to implement our click handler on a tab so we can correctly change tabs when clicked on. For this we're going to use the &lt;code&gt;@Method()&lt;/code&gt; decorator. This decorator exposes class methods on the public API for the component. In other words, you can call those methods simply by querying for the element and calling its method, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mtn-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to keep methods internal to the component we want to simply omit the &lt;code&gt;@Method()&lt;/code&gt; decorator. By my judgement, methods should be kept internal unless there is a valid reason for them to be exposed. In the case of this &lt;code&gt;openTab()&lt;/code&gt; function, there is definitly a good reason to expose it, so let's implement it now. According to the docs, &lt;a href="https://stenciljs.com/docs/methods#public-methods-must-be-async" rel="noopener noreferrer"&gt;public methods must be async&lt;/a&gt; so we're making sure to mark it as such.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;openTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`[mtn-tabs] Index &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is out of bounds of tabs length`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first make sure that the index requested exists. We can't select a tab that doesn't exist. We then check that the tab that has been clicked is not disabled. We don't want a disabled tab to have any functionality. Otherwise we change the &lt;code&gt;active&lt;/code&gt; flag on each tab, depending on whether the index of the tab matches the index we passed into the method. If it matches, we set &lt;code&gt;active&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, otherwise we set it to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you save and check, your tabs now change when you click on them! They're starting to work just how we want them to. There are a few more things, however, that we want to do before we finish up this component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Decorator
&lt;/h2&gt;

&lt;p&gt;We exposed the &lt;code&gt;openTab()&lt;/code&gt; method on the public API of our component. This is obviously useful to be able to programatically change tabs. What about being able to determine when tabs have changed and react to that change? We would use custom Events with the &lt;code&gt;@Event()&lt;/code&gt; decorator.&lt;/p&gt;

&lt;p&gt;With our components, we can define an &lt;code&gt;EventEmitter&lt;/code&gt; to emit &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events" rel="noopener noreferrer"&gt;Custom DOM events&lt;/a&gt;. This is really easy with Stencil. Let's define ours now and emit the changed tab in our &lt;code&gt;openTab()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;openTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`[mtn-tabs] Index &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is out of bounds of tabs length`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created our &lt;code&gt;onChange&lt;/code&gt; which is an &lt;code&gt;EventEmitter&lt;/code&gt; that we imported from Stencil. We also changed its name using the &lt;code&gt;eventName&lt;/code&gt; config property in the decorator. You can change this if you want or keep it the same, it's up to your preference. The only suggestion is that you keep your naming consistent.&lt;/p&gt;

&lt;p&gt;We then call the &lt;code&gt;emit()&lt;/code&gt; function on the &lt;code&gt;EventEmitter&lt;/code&gt; and emit an object with a &lt;code&gt;tabId&lt;/code&gt; of the changed tab. We can now listen to this event outside of this component and react to it, like follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mtn-tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// You'll find your emitted object under event.detail&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CHANGED TABS TO INDEX &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can do things such as swapping out views when the currently active tab changes! Events are super handy when it comes to any good component library. We could add all sorts of events to our components. In the case of our tabs, however, we'll leave it with this one event.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listen Decorator
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@Listen()&lt;/code&gt; decorator is closely related to the &lt;code&gt;@Event()&lt;/code&gt; decorator. Basically it's a shorthand way of listening and reacting to a DOM Event. You could either listen to your own component event you've defined, or you could listen to a standard event, such as the scroll event. It works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body:scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;handleScroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The body was scrolled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're not using this decorator in our tabs component, but it's very simple to use. Other use cases might be a &lt;code&gt;keyup&lt;/code&gt; listener which you could use to, for example, implement the &lt;a href="https://en.wikipedia.org/wiki/Konami_Code" rel="noopener noreferrer"&gt;Konami Code&lt;/a&gt; on your component to unlock a fun easter egg.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch Decorator
&lt;/h2&gt;

&lt;p&gt;There is one more main decorator in Stencil's toolkit, and that's the &lt;code&gt;@Watch()&lt;/code&gt; decorator. It is used to watch a specific property on the component class, and decorates a method which is called upon that property being changed. It calls the method with the &lt;code&gt;oldValue&lt;/code&gt; and &lt;code&gt;newValue&lt;/code&gt;, so you can react with either value in mind. We're not going to use it in our tabs component, but we could if we wanted. Instead of emitting the new index in the &lt;code&gt;openTab()&lt;/code&gt; function, we could instead listen to the tabs property and emit the newly active tab from there, perhaps like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tabs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tabs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;tabsChangeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLMtnTabElement&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newIndex&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that function we don't care about the old value, we just want to return the new tab. This works identically to how we were doing it previously, it still emits the new tab when the tabs change. We're not using this method in our component, however, because we have to search for the index of the new tab each time the tabs are updated, whereas we know for certain which tab is the new one in our &lt;code&gt;openTab()&lt;/code&gt; method, so we'll stick with that. This is how you use the &lt;code&gt;@Watch()&lt;/code&gt; decorator though, and it's useful for other situations and components, so it's worth keeping around in your toolbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Congratulations! Your tabs component should be fully working now! We have a public API to change tabs, an event we can listen and react to, and internal state that helps us re-render appropriately when needed. The decorators we've gone over in this and previous posts form the foundation of building web components with Stencil. We've covered a ton of ground in this series, but there's still more to be had! One of the most important aspects of developing software has been missing thus far, and that's &lt;strong&gt;testing your code&lt;/strong&gt;. We'll be going over that in the next post, coming soon!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Simply want to see the end result repo? Check it out &lt;a href="https://github.com/johnbwoodruff/mountain-ui" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>stencil</category>
      <category>webcomponents</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Component Libraries with Stencil.js - Going Deeper</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Wed, 26 Sep 2018 16:17:05 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---going-deeper-5apm</link>
      <guid>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---going-deeper-5apm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the fourth in a series of posts about creating a web component library using Stencil.js - Check out the &lt;a href="https://dev.to/johnwoodruff91/component-libraries-with-stenciljs---about-stencil-10b7"&gt;first post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the last post we created a very simple button component. We're now going to extend that with more options and functionality to be more fully featured. I personally want to have a couple of color options for this button, a couple of button shapes, and a few sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Props
&lt;/h2&gt;

&lt;p&gt;To start into this, we'll need a few props to allow the user to specify what they want. Let's call our props &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;shape&lt;/code&gt;, and &lt;code&gt;size&lt;/code&gt;. We also can have multiple button types such as submit and reset, just like a regular HTML button, so we'll add that as a prop too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;square&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;round&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;square&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;large&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've barely/not used TypeScript before, you'll notice that instead of defining my types for these props to be of type &lt;code&gt;string&lt;/code&gt;, which technically they are, I'm going a step further and defining that each prop can be one of several specific strings. This allows for much more effective IntelliSense when you're using this component. I'm defining up front a few specific sizes, colors, shapes, and types that I'm going to be implementing. On top of that, I've initialized those props to my preferred defaults, so that if you simply call my button component with no defined props, it'll look like a default button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Class Map
&lt;/h2&gt;

&lt;p&gt;Next I'm going to be using those props to build a map of CSS classes to apply to my component. I typically like to define an interface called &lt;code&gt;CssClassMap&lt;/code&gt; for this purpose. Let's create that file at &lt;code&gt;src/utils/interfaces.ts&lt;/code&gt;, as it's something I'll be reusing for all my components, and use the following content for the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CssClassMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I'm defining this class map type as a map of a key, the class name, and the value being a boolean. Effectively I'm going to be specifying whether I want classes to be applied or not. Now that we have this interface, let's write a method that builds this class map on render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCssClassMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classMap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;getCssClassMap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;CssClassMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I'm calling our function from the render method to get the class map, a fairly simple one in this case, and am applying that to the &lt;code&gt;button&lt;/code&gt;. You may also have noticed that I also passed the &lt;code&gt;type&lt;/code&gt; prop to the button as well. Now that we have the correct classes being applied, let's actually implement those classes. First we need the correct colors, so let's add a few more SCSS variables to our &lt;code&gt;src/styles/variables.scss&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;

&lt;span class="c1"&gt;// Brand Colors&lt;/span&gt;
&lt;span class="nv"&gt;$blue-steel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#4571c4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$blue-steel-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#315db0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$coral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#c75943&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$coral-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#a83a24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f0f1f2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$light-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#e2e3e4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the variables available, let's implement the classes themselves. We don't need to implement the default classes as they are the default that we've already implemented.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;

  &lt;span class="c1"&gt;// Color variations&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.accent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$coral&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$coral-dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$coral-dark&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.light&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$light-dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$light-dark&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Shape variations&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.round&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Size variations&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.small&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.large&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the color changing classes, we need to re-implement our &lt;code&gt;hover&lt;/code&gt; and &lt;code&gt;active&lt;/code&gt; states. We do not need to re-implement our &lt;code&gt;disabled&lt;/code&gt; state since our existing one simply applies a lower opacity, looking great in any color. Making the button round is trivial, and then we finally tweak the &lt;code&gt;padding&lt;/code&gt; and &lt;code&gt;font-size&lt;/code&gt; of our button to look good as a large or small button.&lt;/p&gt;

&lt;p&gt;Now that we've implemented these classes, we have some pretty incredible looking button web components!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g92md0XI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/m0yrzxl4jfk994sj6a7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g92md0XI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/m0yrzxl4jfk994sj6a7b.png" alt="buttons" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;We now have an awesome button component we can use anywhere! This is a simple first component, so we're not dealing with many of the other decorators such as &lt;code&gt;@Watch()&lt;/code&gt;, &lt;code&gt;@Method()&lt;/code&gt;, or &lt;code&gt;@State()&lt;/code&gt;. We'll get into those with a new component that is more complex in our next post. See you in the next one!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Simply want to see the end result repo? Check it out &lt;a href="https://github.com/johnbwoodruff/mountain-ui"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>stencil</category>
      <category>webcomponents</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Component Libraries with Stencil.js - Your First Component</title>
      <dc:creator>John Woodruff</dc:creator>
      <pubDate>Mon, 17 Sep 2018 15:57:09 +0000</pubDate>
      <link>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---your-first-component-3b7p</link>
      <guid>https://dev.to/johnbwoodruff/component-libraries-with-stenciljs---your-first-component-3b7p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the third in a series of posts about creating a web component library using Stencil.js - Check out the &lt;a href="https://dev.to/johnwoodruff91/component-libraries-with-stenciljs---about-stencil-10b7"&gt;first post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We've done a lot of setup so far, now let's create our first component. This is the foundation of any component library: the button component. Let's rename the &lt;code&gt;my-component&lt;/code&gt; folder to &lt;code&gt;button&lt;/code&gt;, and all the files inside replacing &lt;code&gt;my-component&lt;/code&gt; to &lt;code&gt;button&lt;/code&gt;. We're now ready to build our button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Decorator
&lt;/h2&gt;

&lt;p&gt;The first thing we're going to do is change the contents of &lt;code&gt;button.tsx&lt;/code&gt;. We're going to change the &lt;code&gt;tag&lt;/code&gt; in the &lt;code&gt;@Component&lt;/code&gt; decorator to our actual tag name, and the &lt;code&gt;styleUrl&lt;/code&gt; to point to our newly renamed SCSS file, in my case the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mtn-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;shadow&lt;/code&gt; property. This is declaring that we will be using the &lt;a href="https://developers.google.com/web/fundamentals/web-components/shadowdom"&gt;Shadow DOM&lt;/a&gt; for this component. This has many benefits including an isolated DOM and scoped CSS, among others. We'll definitely want to be taking advantage of this, as it's one of the key parts of web components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render Method/JSX
&lt;/h2&gt;

&lt;p&gt;Next we're going to change the class to render a plain button with a single prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the render method, you'll notice we're not writing HTML. We're using a JavaScript syntax extension called &lt;a href="https://reactjs.org/docs/introducing-jsx.html"&gt;JSX&lt;/a&gt; or JavaScript XML. Note with Stencil we're actually using TSX, a file with the ability to write JSX using TypeScript. Let's change the markup to render a standard HTML button. We're also placing a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot"&gt;slot&lt;/a&gt; inside the button. This is a part of the suite of browser web component APIs; it allows us to fill that slot with markup defined by the consumer of the component. In our case, consuming this component would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;mtn-button&amp;gt;&lt;/span&gt;Click Me!&lt;span class="nt"&gt;&amp;lt;/mtn-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text "Click Me!" is projected down to the slot within the component. The markup in the component will be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&lt;/span&gt;Click Me!&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This button component is currently incredibly simple. With more complex components we will obviously have more markup, and will occasionally use multiple named slots to project multiple bits of markup down to the component. For now, we'll stick with that for our button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Props
&lt;/h2&gt;

&lt;p&gt;You'll have noticed by now if you're familiar at all with React that these components look and function very similarly to React components. Along those lines, we're now going to define a prop. An important piece of functionality for a button is to disable that button. Let's use the &lt;code&gt;@Prop()&lt;/code&gt; decorator to define a &lt;code&gt;disabled&lt;/code&gt; property. We're also going to pass that disabled property down to our actual button. We'll update our class like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reflectToAttr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've defined a property on the class called &lt;code&gt;disabled&lt;/code&gt;, and added a &lt;code&gt;@Prop()&lt;/code&gt; decorator to it. We've also passed in an object with a &lt;code&gt;reflectToAttr&lt;/code&gt; key we've defined to be true. According to the &lt;a href="https://stenciljs.com/docs/properties#reflect-properties-values-to-attributes"&gt;Stencil Prop Docs&lt;/a&gt;, using that makes sure our disabled prop stays in sync with an HTML attribute. In this case we definitely want that because we're using a disabled attribute.&lt;/p&gt;

&lt;p&gt;We are also adding &lt;code&gt;disabled={this.disabled}&lt;/code&gt; to our button in the component. This will conditionally apply the disabled attribute depending on whether or not the disabled prop is defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling Our Button
&lt;/h2&gt;

&lt;p&gt;Currently this is an ugly HTML button. We're obviously going to change that. I encourage you to come up with your own style guide and design for your components, but you're of course welcome to copy what I'm doing. First off I'm going to create a file at &lt;code&gt;src/styles/variables.scss&lt;/code&gt; for my color variables. If I have to change colors, I want it to apply to all the colors rather than change them one by one, so I'm using SASS variables to do that. I'm going to start with my font, a couple basic colors, and my primary color shades which is all I'm focusing on right now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Font Family&lt;/span&gt;
&lt;span class="nv"&gt;$font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Open Sans'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Helvetica Neue'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Basic Colors&lt;/span&gt;
&lt;span class="nv"&gt;$white&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$black&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Brand Colors&lt;/span&gt;
&lt;span class="nv"&gt;$blue-steel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#4571c4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$blue-steel-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#315db0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I've got my font and colors established, I'm going to give my button some classy styling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;'../../styles/variables.scss'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$font-family&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$blue-steel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$blue-steel-dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$blue-steel-dark&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:disabled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="mi"&gt;.4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First I'm importing my variables for use. I'm then using the &lt;code&gt;:host&lt;/code&gt; selector, which allows us to select the shadow host of the Shadow DOM, for a couple different things. First of all I'm setting &lt;code&gt;box-sizing: border-box&lt;/code&gt; to the host element. I personally prefer using the border-box sizing to not take into account borders for the height and width. It makes more sense to me personally. Next I'm using the host selector to &lt;em&gt;only&lt;/em&gt; select the host when it has a &lt;code&gt;disabled&lt;/code&gt; attribute applied to it. In that case I'm applying &lt;code&gt;pointer-events: none&lt;/code&gt; to the element. This makes it so setting a click handler on the element will not fire when the button is disabled.&lt;/p&gt;

&lt;p&gt;Next I'm styling my button itself. You'll notice and possibly worry about me globally styling the &lt;code&gt;button&lt;/code&gt; element. This is not a problem because we're using the Shadow DOM, and this is one of its best benefits. All of your styling is scoped to the component's Shadow DOM, no styles from outside can penetrate it, and no styles from inside can mess up anything outside of it. It's pretty awesome.&lt;/p&gt;

&lt;p&gt;The rest of the styles are pretty straightforward. I have the button styles, the background color darkens on hover, and darkens even more on click. When it's disabled, I apply &lt;code&gt;opacity: 0.4&lt;/code&gt; to make it look disabled.&lt;/p&gt;

&lt;p&gt;If you haven't already, make sure you run &lt;code&gt;npm start&lt;/code&gt; to start up your dev server and it'll automatically open in your browser. Go ahead and add your button to your &lt;code&gt;index.html&lt;/code&gt; page so you can test your component. I added the following markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;mtn-button&amp;gt;&lt;/span&gt;Button&lt;span class="nt"&gt;&amp;lt;/mtn-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;mtn-button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Button&lt;span class="nt"&gt;&amp;lt;/mtn-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your page refreshes automatically you should see your beautiful new button component in both its disabled and non-disabled states!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AscqgZmT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mq6160q4l4v5cu7b0ode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AscqgZmT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mq6160q4l4v5cu7b0ode.png" alt="new buttons" width="476" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;There you have it! A classy button component that looks great and has basic functionality. There's obviously a lot more to a button (and most components) that will implement, but we'll do that in our &lt;a href="https://dev.to/johnwoodruff91/component-libraries-with-stenciljs---going-deeper-5apm"&gt;next post&lt;/a&gt;. See you there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Simply want to see the end result repo? Check it out &lt;a href="https://github.com/johnbwoodruff/mountain-ui"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>stencil</category>
      <category>webcomponents</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
