<?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: Bryan Lee</title>
    <description>The latest articles on DEV Community by Bryan Lee (@bryanleetc).</description>
    <link>https://dev.to/bryanleetc</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%2F188052%2F500cd97b-d92d-42a5-9d63-c8901898e48f.png</url>
      <title>DEV Community: Bryan Lee</title>
      <link>https://dev.to/bryanleetc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bryanleetc"/>
    <language>en</language>
    <item>
      <title>Migrating Hugo to Eleventy - Initial thoughts</title>
      <dc:creator>Bryan Lee</dc:creator>
      <pubDate>Sat, 29 Feb 2020 18:34:24 +0000</pubDate>
      <link>https://dev.to/bryanleetc/migrating-hugo-to-eleventy-initial-thoughts-12ih</link>
      <guid>https://dev.to/bryanleetc/migrating-hugo-to-eleventy-initial-thoughts-12ih</guid>
      <description>&lt;p&gt;I've just spent one afternoon and a couple of hours after dinner migrating this blog from Hugo to Eleventy and also refreshing the design a little. The paint has not even dried yet but I am really amazed and delighted about how enjoyable and easy the migration was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Eleventy?
&lt;/h2&gt;

&lt;p&gt;Hugo has been perfectly fine for my blog, but as my projects at work are going towards more JAMStack, I thought I'd update myself about the latest developments in this area. Eleventy caught my attention primarily due to it being written in JavaScript which is something I am extremely at home with.&lt;/p&gt;

&lt;p&gt;The other very interesting point about Eleventy is that it aims to be as agnostic as possible. It does not take a stance between React or Vue, and you can freely choose to write your layouts in Liquid, Nunjucks, Pug, or just plain HTML. You can also have different layouts written in different languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smooth setup and migration process
&lt;/h2&gt;

&lt;p&gt;The whole process from installing Eleventy to publishing it live on Netlify took me 5-6 hours, not bad considering that I also rewrote the HTML and CSS again from scratch. Disclaimer alert - my blog is a simple website.&lt;/p&gt;

&lt;p&gt;Migrating my blog post markdown files over was fairly easy. I had to tweak the front matter a little to make them compatible with Eleventy. I was using &lt;code&gt;tags&lt;/code&gt; in Hugo for grouping my posts, but &lt;code&gt;tags&lt;/code&gt; are used by &lt;a href="https://www.11ty.dev/docs/collections/"&gt;Eleventy Collections&lt;/a&gt; in a slightly different manner and didn't really transfer over. I had to rename a couple of other attributes too, but nothing too difficult for a few full-text search and replaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Webpack
&lt;/h2&gt;

&lt;p&gt;One thing you'll quickly notice is the glaring absence of Webpack. I needed to handle image assets and also to use SCSS. No biggie too, there is already a SASS plugin for Eleventy. Eleventy does not support SVG files by default but there is also a community plugin to cover this. No Webpack was involved at all and I found this really refreshing without having to fiddle with Webpack config files and getting my hands all greasy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/eleventy-plugin-sass"&gt;Eleventy Sass plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.11ty.dev/docs/plugins/syntaxhighlight/"&gt;Eleventy syntax highlighting plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/eleventy-plugin-svg-contents"&gt;Eleventy SVG contents plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intuitive templating system and awesome documentation
&lt;/h2&gt;

&lt;p&gt;I rewrote the HTML and CSS from scratch with some tweaks to the design and found the Eleventy documentation to be really well-written. I loved how you can just chain layouts together to create different variations. The directory structure is also really simple. All layouts go into the &lt;code&gt;_includes&lt;/code&gt; folder, and you specify in your front matter which layout you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Specifying layouts&lt;/span&gt;
&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;layout.liquid&lt;/span&gt; &lt;span class="c1"&gt;# Loads layout from _includes/layout.liquid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Eleventy also has this concept of Data Cascading to merge data from various sources. You can use this to specify that you want all your blog posts to load a different template from your sitewide default, and you can also even load a post-specific template. I like this declarative approach much more than directory-based inheritance approach which Hugo used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;posts/posts.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;affects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;content&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;span class="nl"&gt;"layout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blog-post.liquid"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;layout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_includes/blog-post.liquid&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;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# posts/special-post.md&lt;/span&gt;
&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;special-occasion-layout.liquid&lt;/span&gt; &lt;span class="c1"&gt;# Only this post will use a different layout from the rest of your posts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Unopinionated and flexible
&lt;/h2&gt;

&lt;p&gt;Eleventy comes with support for a range of templating languages out of the box (Liquid, Nunjucks, HAML etc). As I wrote in the beginning of this post, you can also use different languages for different layouts. I loved not being forced into a templating language that I'm not familiar with or one that I'm not a fan of. I decided to go with Liquid (yay Shopify) which itself is also really pleasant to work with.&lt;/p&gt;

&lt;p&gt;I also see how Eleventy is just plain Javascript and not built on top of React, Vue or any other framework a huge plus. Given how fast things move in the frontend world, you could wake up tomorrow and X framework is no longer cool. Plain old JavaScript will always be here, and I can always write my own Eleventy plugins without having to keep up with a framework I may not be familiar with.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the bad?
&lt;/h2&gt;

&lt;p&gt;I've been singing praises for Eleventy this entire post. Does this mean Eleventy is the best static site generator there? I definitely don't think so. I've still not spent a lot of time with Eleventy yet. My experience so far has been these couple of hours migrating from Hugo although it has been a very pleasant one indeed. My blog is also a very simple website which also means I am not pushing Eleventy to its limits yet. As I play around more, the parts where Eleventy do not do well will start to be more visible and I hope to do another post on this then.&lt;/p&gt;

&lt;p&gt;This post was first written on my &lt;a href="https://www.bryanleetc.com"&gt;blog&lt;/a&gt; where you can find more posts related to frontend development.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>eleventy</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Creating a Share Extension in NativeScript</title>
      <dc:creator>Bryan Lee</dc:creator>
      <pubDate>Thu, 03 Oct 2019 13:56:09 +0000</pubDate>
      <link>https://dev.to/bryanleetc/creating-a-share-extension-in-nativescript-499k</link>
      <guid>https://dev.to/bryanleetc/creating-a-share-extension-in-nativescript-499k</guid>
      <description>&lt;p&gt;Share extensions are a type of app extension in iOS that extend functionality to your app. Users will be able to share other forms of content with your app. Think sharing an article you enjoy in Safari over WhatsApp, your photos over Instagram. Nativescript as of version  5.3 has also introduced beta support for share extensions. I have worked with NativeScript to launch a share extension enabled app into production and found the overall process pleasant.&lt;/p&gt;

&lt;h2&gt;
  
  
  A primer to share extensions
&lt;/h2&gt;

&lt;p&gt;Before diving into creating a share extension, let's understand how share extensions work. Share extensions are bundled together with your main app but have their own life cycle and environment. Unlike your host app, the share extension is not run until the user chooses it from the iOS share sheet. One other thing to note is that the share extension does not communicate or share data directly with the host app. In iOS, related apps and extensions share data via App Groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a share extension in NativeScript
&lt;/h2&gt;

&lt;p&gt;Creating a share extension in NativeScript is largely similar to how you would do it natively via Xcode. If you already have experience with doing it natively, feel free to skip straight to the NativeScript-specific part. Otherwise, the process looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Xcode to scaffold a share extension&lt;/li&gt;
&lt;li&gt;Integrating it into NativeScript's build process&lt;/li&gt;
&lt;li&gt;Coding your share extension&lt;/li&gt;
&lt;li&gt;Sharing data with your host app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scaffolding a share extension with Xcode
&lt;/h3&gt;

&lt;p&gt;First, we will want to use Xcode to generate a scaffold of our share extension. This will give us a basic working share extension. Open your NativeScript app's iOS Xcode project located in &lt;code&gt;platforms/ios&lt;/code&gt;. NativeScript uses CocoaPods, so make sure you open the &lt;code&gt;.xcworkspace&lt;/code&gt; file instead of your &lt;code&gt;.xcodeproj&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;There are already countless guides out in the wild documenting how to do this in Xcode, so I will link you to the &lt;a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionCreation.html"&gt;official Apple one&lt;/a&gt; instead of repeating it here. Feel free to go with either Swift or Objective-C as both will work with NativeScript.&lt;/p&gt;

&lt;p&gt;After this, you will need to setup an App Group to share data between your extension and host app. In Xcode, go to your project's capabilities tab to create a new App Group. In your list of project targets, go to your main host app and enable the App Group for it as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating your share extension into your NativeScript app
&lt;/h3&gt;

&lt;p&gt;You have a working share extension in your Xcode project already, but it is not yet integrated into NativeScript. However, as this is created inside Xcode, NativeScript will override them the next time you run &lt;code&gt;tns prepare&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Xcode, you will see a newly generated folder for your share extension. Replicate the contents inside to &lt;code&gt;app/App_Resources/iOS/extensions/your_share_extension_folder&lt;/code&gt;. Now when you run &lt;code&gt;tns prepare&lt;/code&gt;, it is copied into your Xcode project.&lt;/p&gt;

&lt;p&gt;When you build and run your app in Simulator now, you should see a new share extension for your app when you open up the share sheet in Safari. Congratulations!&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieving share extension data in your NativeScript app
&lt;/h3&gt;

&lt;p&gt;This guide will not go into the specifics of coding out the share extension as well. Instead, let's explore how we can retrieve data shared from your extension. To recap what we have done previously, we created an App Group, which will let us share data between the extension and host app.&lt;/p&gt;

&lt;p&gt;In your extension, you will probably be saving data into the App Group via UserDefaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:GROUP_IDENTIFIER];
[self.userDefaults setObject:shareJsonString forKey:@"share"];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In NativeScript, the Application Settings module is used to interact with iOS's NSUserDefaults. However, Application Settings is limited to only interacting with the app's User Defaults. To do so with our App Group, we will have to use the native APIs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&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;isIOS&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;tns-core-modules/platform&lt;/span&gt;&lt;span class="dl"&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;userDefaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isIOS&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;NSUserDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;initWithSuiteName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;group.com.yourapp.shareext&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="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Retrieving&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;
&lt;span class="nx"&gt;userDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueForKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Debugging your share extension
&lt;/h3&gt;

&lt;p&gt;When using Nativescript CLI to build and run your app, you will see that the console only outputs logs from your host app. Should you need more in-depth logging or utilise other debugging strategies like breakpoints, you will need to target the share extension in Xcode and run it. Share extension related logs will be output in the Xcode console.&lt;/p&gt;

&lt;p&gt;Overall, the process of developing a share extension in NativeScript went quite smoothly for me. Though support by NativeScript is only in beta, it is good enough to be deployed into the wild as a production app. Creating a share extension is a complex topic by itself and even more so doing it in NativeScript. I am sure I have overlooked some details in writing this guide. Please feel free to reach out if there is something else I can clarify more on or if you have any questions / feedback.&lt;/p&gt;

&lt;p&gt;This post was first written on my &lt;a href="https://www.bryanleetc.com/creating-a-share-extension-in-nativescript/"&gt;personal blog&lt;/a&gt; where you can find more posts related to NativeScript development. Feel free to also reach out to me directly over &lt;a href="https://www.linkedin.com/in/bryan-tc/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/bryanleetc"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nativescript</category>
      <category>javascript</category>
      <category>ios</category>
    </item>
    <item>
      <title>Vue Renderless Component Fun: Google Analytics Event Tracker</title>
      <dc:creator>Bryan Lee</dc:creator>
      <pubDate>Sun, 30 Jun 2019 09:29:45 +0000</pubDate>
      <link>https://dev.to/bryanleetc/vue-renderless-component-fun-google-analytics-event-tracker-5e2p</link>
      <guid>https://dev.to/bryanleetc/vue-renderless-component-fun-google-analytics-event-tracker-5e2p</guid>
      <description>&lt;p&gt;Let's say you needed to track how many times a specific button in your Vue app is clicked on using Google Analytics, how would you do it? The first thing that comes to mind might be with a click handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"onClick"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Important Button&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ga&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;send&lt;/span&gt;&lt;span class="dl"&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;event&lt;/span&gt;&lt;span class="dl"&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;Video&lt;/span&gt;&lt;span class="dl"&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;play&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Other things the button does when clicked&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 a perfectly fine approach, but as you start tracking more button and link clicks, this can get cumbersome. With renderless components in Vue, we can abstract the event tracking into its own easy-to-reuse component.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Renderless Component
&lt;/h2&gt;

&lt;p&gt;A normal Vue component has a HTML template, Javascript and CSS. A renderless component does not have its own markup. It usually has a render function that renders a scoped slot. Data and function from the renderless component gets passed to the child component through the scoped slot. This simple concept essentially lets you to abstract out complex behaviors that can be reused with different components.&lt;/p&gt;

&lt;p&gt;To illustrate this better, we will build a renderless component that you can wrap around buttons, links and other elements to track clicks in Google Analytics. Let's start off by scaffolding a renderless component we shall call &lt;code&gt;Track&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Renderless component&lt;/span&gt;
&lt;span class="c1"&gt;// Track.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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="nx"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$slots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.vue&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onClick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Important&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/track&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Track&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;track&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="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Track&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// the button's own click handler function&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We import our new &lt;code&gt;Track&lt;/code&gt; component into our &lt;code&gt;App&lt;/code&gt; component, and wrap it around the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element which we intend to track. &lt;code&gt;Track&lt;/code&gt; here is a very basic renderless component. All it does is render a default slot, in this case, rendering the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; which we've wrapped around with. If you refresh your page now, you would not notice any difference. The &lt;code&gt;Track&lt;/code&gt; component is transparently rendering the child button, it has no markup of its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listening to Clicks
&lt;/h2&gt;

&lt;p&gt;Now, we want to start making our &lt;code&gt;Track&lt;/code&gt; component useful. We want it to listen for clicks on its child element and then send call the Google Analytics API when there is one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; Track.js
&lt;span class="p"&gt;export default {
&lt;/span&gt;  render() {
    return $this.$slots.default;
  },
&lt;span class="gi"&gt;+ mounted() {
+   this.$slots.default[0].elm.addEventListener('click', this.trackClick, true);
+ },
+  methods: {
+    trackClick() {
+      ga('send', 'event', 'Video', 'play');
+    }
+  }
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's go through what we just did. We are declaring in our component's &lt;code&gt;mounted&lt;/code&gt; lifecycle hook after it is rendered that we are adding an event listener. &lt;code&gt;this.$slots.default&lt;/code&gt; means that we are accessing the component's default slot. We are looking for only the first element, and we add an event listener for &lt;code&gt;click&lt;/code&gt; events. If there is a click, we run the &lt;code&gt;trackClick&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The last argument in &lt;code&gt;addEventListener&lt;/code&gt; is true says we want to use event capturing instead of the default event bubbling. Event capture means that events are dispatched top down the DOM tree to our listener instead of the default bubbling up the DOM tree. In actual effect, this lets us also listen to clicks even if there is a &lt;code&gt;preventDefault&lt;/code&gt; declared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it More Reusable
&lt;/h2&gt;

&lt;p&gt;There is one slight issue left. What if we want to track another link in another page? Looking at our code again, all clicks will be calling this &lt;code&gt;ga('send', 'event', 'Video', 'play')&lt;/code&gt;. Let's make it so that we can customise the different variables we want to send over to Google Analytics (GA) using props. We shall also follow the standard fields set by the &lt;a href="https://developers.google.com/analytics/devguides/collection/analyticsjs/events"&gt;GA API&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; Track.js
&lt;span class="p"&gt;export default {
&lt;/span&gt;  render() {
    return $this.$slots.default;
  },
&lt;span class="gi"&gt;+ props: [
+   eventCategory,
+   eventAction,
+   eventLabel,
+   eventValue,
+ ],
&lt;/span&gt;  mounted() {
    this.$slots.default[0].elm.addEventListener('click', this.trackClick, true);
  },
  methods: {
    trackClick() {
&lt;span class="gd"&gt;-     ga('send', 'event', 'Video', 'play');
&lt;/span&gt;&lt;span class="gi"&gt;+     ga('send', 'event', eventCategory, eventAction, eventLabel, eventValue);
&lt;/span&gt;    }
  }
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we can use it in different places with the relevant event fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Track&lt;/span&gt; &lt;span class="na"&gt;eventCategory=&lt;/span&gt;&lt;span class="s"&gt;"Checkout"&lt;/span&gt; &lt;span class="na"&gt;eventAction=&lt;/span&gt;&lt;span class="s"&gt;"Button Click"&lt;/span&gt; &lt;span class="na"&gt;eventLabel=&lt;/span&gt;&lt;span class="s"&gt;"Sidebar"&lt;/span&gt; &lt;span class="na"&gt;eventValue=&lt;/span&gt;&lt;span class="s"&gt;"$30"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Some button&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Track&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Happy Tracking!
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;Track&lt;/code&gt; component is now ready to use in other parts of the code. One advantage of abstracting out external APIs you gain is that code becomes more maintainable now. Google made an update to the GA API that requires an update? Instead  of updating the 30 different places where the API call is used, you just update it in &lt;code&gt;Track&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another huge plus, your code base is more inclusive. Previously, a new developer that joins your team will also need some familiarity with the GA API on top of their Vue skills. Abstracting this into an intuitive concept that most Vue developers are already familiar with equals to more productivity from day one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://www.bryanleetc.com/vue-renderless-component-fun-google-analytics-event-tracker/"&gt;Bryan Lee&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
