<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Vuejs on Techformist</title>
    <link>https://techformist.com/tags/vuejs/</link>
    <description>Recent content in Vuejs on Techformist</description>
    <image>
      <url>https://techformist.com/logo.svg</url>
      <link>https://techformist.com/logo.svg</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 02 Oct 2025 06:30:00 +0000</lastBuildDate><atom:link href="https://techformist.com/tags/vuejs/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Notocan is a simple, local Google Keep alternative</title>
      <link>https://techformist.com/notocan-simple-local-notes/</link>
      <pubDate>Thu, 02 Oct 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/notocan-simple-local-notes/</guid>
      <description>&lt;p&gt;After spending years 😏 building applications with Nuxt.js (and occasionally dipping into Next.js), I decided to take a step back to pure Vue 3 and create a.. lo and behold - a Google Keep alternative.&lt;/p&gt;
&lt;p&gt;Just kidding - the app is far more dumb.&lt;/p&gt;
&lt;h2 id=&#34;notocan-a-dumb-google-keep-alternative&#34;&gt;Notocan: A Dumb Google Keep Alternative&lt;/h2&gt;
&lt;p&gt;Notocan is what I like to say &amp;ldquo;deliberately dumb&amp;rdquo;, local and fast. It&amp;rsquo;s not trying to be the next big productivity platform with AI features, cloud sync complexity, or fancy collaboration tools. It&amp;rsquo;s just notes, done right in a browser window.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>After spending years 😏 building applications with Nuxt.js (and occasionally dipping into Next.js), I decided to take a step back to pure Vue 3 and create a.. lo and behold - a Google Keep alternative.</p>
<p>Just kidding - the app is far more dumb.</p>
<h2 id="notocan-a-dumb-google-keep-alternative">Notocan: A Dumb Google Keep Alternative</h2>
<p>Notocan is what I like to say &ldquo;deliberately dumb&rdquo;, local and fast. It&rsquo;s not trying to be the next big productivity platform with AI features, cloud sync complexity, or fancy collaboration tools. It&rsquo;s just notes, done right in a browser window.</p>
<p><img loading="lazy" src="/2025/notocan-keep-alternative.png" type="" alt="notocan-local-notes-in-browser"  /></p>
<p>tldr;</p>
<ul>
<li>Has it been done a million times? Yes.</li>
<li>Has it been done by me in the past? Probably, yes.</li>
<li>Will it deter me from building something dumb again. Absolutely not.</li>
</ul>
<p>The core features are straightforward:</p>
<ul>
<li>Create notes with titles and rich text content</li>
<li>Color-code your notes for visual organization</li>
<li>Pin important notes to keep them at the top</li>
<li>Archive notes you don&rsquo;t need anymore</li>
<li>Search through all your notes instantly</li>
<li>Keyboard navigation</li>
</ul>
<p>Check it out here:
<a href="https://notocan.techformist.com">https://notocan.techformist.com</a></p>
<p>If you are gungho about todo&rsquo;s and notes - Notocan is open source and available on <a href="https://github.com/prashanth1k/notocan">GitHub</a>.</p>
<p>Feel free to use the app, or just poke around the code!</p>
<h2 id="back-to-the-experience">Back to the Experience</h2>
<p>The experience though has been nothing short of refreshing. No more complex configurations, no more wrestling with SSR setups and errors, just pure Vue 3 with the Composition API.</p>
<p>The dev experience is incredibly fast - hot reload is instantaneous, compilation is lightning-quick, and the build process feels almost instantaneous.</p>
<p>It&rsquo;s amazing how much mental overhead you shed when you don&rsquo;t have to think about server-side rendering for a client-side notes app. Also, there are no accounts, no subscriptions, no worrying about data privacy or service outages.</p>
<p>Your notes live in your browser&rsquo;s IndexedDB, which means:</p>
<ul>
<li>Instant loading (no network calls)</li>
<li>Works offline perfectly</li>
<li>Your data stays yours</li>
<li>Migration from localStorage is handled automatically</li>
</ul>
<p>The IndexedDB implementation includes pagination for performance, so even with hundreds of notes, the app stays snappy.</p>
<p>Also, I can&rsquo;t say enough good things about DaisyUI. Every time I use it, I&rsquo;m reminded why it was my go-to UI library. The component system is thoughtful and the default styles just work without being overwhelming.</p>
<p>Combined with Tailwind CSS, DaisyUI makes building polished interfaces feel effortless. The color system, spacing, and component variants work together seamlessly. It&rsquo;s the kind of library that gets out of your way and lets you focus on building features, not fighting CSS. And, not having all the code in my repo mocking my design skills. I would like that mocking to be deep insite the node_modules thank you very much.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Building Notocan reinforced some important lessons:</p>
<ol>
<li>Simple is indeed beautiful</li>
<li>I continue to be undeterred by no one using the applications I build</li>
</ol>
<p>If you need a simple notes app, check out Notocan - it might just become your new favorite way to jot down thoughts.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing utils@techformist - A Developer&#39;s Swiss Army Knife</title>
      <link>https://techformist.com/free-developer-tools/</link>
      <pubDate>Tue, 25 Mar 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/free-developer-tools/</guid>
      <description>&lt;p&gt;Today, I&amp;rsquo;m excited to introduce &lt;strong&gt;utils.techformist.com&lt;/strong&gt; – a collection of client-side web utilities designed to make developers&amp;rsquo; lives easier. Built with speed, simplicity, and privacy in mind, this tool suite aims to be your go-to resource for common development tasks.&lt;/p&gt;
&lt;h2 id=&#34;why-another-utility-site&#34;&gt;Why Another Utility Site?&lt;/h2&gt;
&lt;p&gt;The web is full of utility sites, but many come with drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intrusive ads and analytics&lt;/li&gt;
&lt;li&gt;Server-side processing of potentially sensitive data&lt;/li&gt;
&lt;li&gt;Slow loading times&lt;/li&gt;
&lt;li&gt;Inconsistent UI/UX&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://utils.techformist.com&#34;&gt;utils.techformist.com&lt;/a&gt; addresses these issues by keeping everything client-side, ensuring your data never leaves your browser. The site loads quickly, works offline, and offers a clean, consistent interface for all tools.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Today, I&rsquo;m excited to introduce <strong>utils.techformist.com</strong> – a collection of client-side web utilities designed to make developers&rsquo; lives easier. Built with speed, simplicity, and privacy in mind, this tool suite aims to be your go-to resource for common development tasks.</p>
<h2 id="why-another-utility-site">Why Another Utility Site?</h2>
<p>The web is full of utility sites, but many come with drawbacks:</p>
<ul>
<li>Intrusive ads and analytics</li>
<li>Server-side processing of potentially sensitive data</li>
<li>Slow loading times</li>
<li>Inconsistent UI/UX</li>
</ul>
<p><a href="https://utils.techformist.com">utils.techformist.com</a> addresses these issues by keeping everything client-side, ensuring your data never leaves your browser. The site loads quickly, works offline, and offers a clean, consistent interface for all tools.</p>
<p><img loading="lazy" src="/2025/great-developer-tools-free.png" type="" alt="great-developer-tools-free"  /></p>
<h2 id="key-features">Key Features</h2>
<h3 id="1-tools-that-you-want-to-use">1. Tools that you want to use!</h3>
<p>Our collection includes essential utilities that solve real problems :)</p>
<ul>
<li><strong>Calculator</strong>: A simple yet powerful calculator for quick arithmetic operations.</li>
<li><strong>QR Code Generator</strong>: Create QR codes for URLs, WiFi networks, contact details, and more with customizable colors.</li>
<li><strong>Password Generator</strong>: Generate secure, random passwords with options for length, character types, and complexity.</li>
<li><strong>Barcode Generator</strong>: Create various barcode formats with customizable settings for professional or personal use.</li>
<li><strong>Image Converter</strong>: Convert images between different formats without uploading them to any server.</li>
<li><strong>Favicon Generator</strong>: Quickly create favicons for your website from any image.</li>
<li><strong>Color Picker</strong>: Extract and analyze colors from images with support for multiple color formats.</li>
<li><strong>JSON Formatter</strong>: Format, validate, and beautify JSON data with easy-to-use controls and copy functionality.</li>
<li><strong>Base64 Converter</strong>: Encode and decode text or files to and from Base64 format right in your browser.</li>
</ul>
<p>.. and so on. Check out the site for the latest updates.</p>
<h3 id="2-fast-search-and-category-based-organization">2. Fast Search and Category-Based Organization</h3>
<p>A simple tool search is at the heart of the site!
At the same time, tools are organized into logical categories for easy discovery:</p>
<ul>
<li><strong>Code</strong>: Developer tools like JSON Formatter and Base64 Converter for everyday coding tasks</li>
<li><strong>Image</strong>: Tools for working with images, including QR Code Generator, Barcode Generator, Image Converter, and Favicon Generator</li>
<li><strong>Text</strong>: Text manipulation utilities like the Password Generator</li>
<li><strong>Design</strong>: Creative tools such as the Color Picker for design work</li>
<li><strong>File</strong>: File processing tools like the PDF Compressor</li>
<li><strong>Other</strong>: Miscellaneous utilities including the Calculator</li>
</ul>
<p>This categorization ensures you can quickly find the right tool for your current task.</p>
<h3 id="3-responsive-and-accessible-design">3. Responsive and Accessible Design</h3>
<p>All tools feature:</p>
<ul>
<li>Fully responsive layouts that work on devices of any size</li>
<li>Dark/light mode support</li>
</ul>
<h3 id="4-privacy-first-approach">4. Privacy-First Approach</h3>
<p>What sets util.techformist.com apart is its commitment to privacy:</p>
<ul>
<li>No tracking (except essentials)</li>
<li>No server-side processing</li>
<li>No data storage outside of the browser</li>
<li>No data transmission to any external computer outside of your own browser</li>
</ul>
<h2 id="technology-stack">Technology Stack</h2>
<p>Utils.techformist.com is built on modern web technologies:</p>
<ul>
<li><strong>Nuxt 3</strong>: For its excellent developer experience, automatic routing, and performance optimizations</li>
<li><strong>NuxtUI</strong>: Providing a consistent design system and accessible components</li>
<li><strong>Tailwind CSS</strong>: For rapid styling and responsive design</li>
</ul>
<p>The app is client-side only - no database or server processing.</p>
<h2 id="why-client-side-only">Why Client-Side Only?</h2>
<p>The decision to make util.techformist.com entirely client-side was deliberate:</p>
<ol>
<li><strong>Privacy</strong>: Your data never leaves your browser, eliminating privacy concerns.</li>
<li><strong>Performance</strong>: No server roundtrips means instant processing of your tasks.</li>
<li><strong>Availability</strong>: The site works offline once loaded.</li>
<li><strong>Scalability</strong>: Without server costs, the site can scale infinitely.</li>
<li><strong>Simplicity</strong>: The architecture remains clean without databases or APIs.</li>
</ol>
<h2 id="development-approach">Development Approach</h2>
<p>Building the site was straightforward with Nuxt&rsquo;s module system and NuxtUI&rsquo;s component library. The development workflow included:</p>
<ol>
<li>Setting up a Nuxt 3 project with the content and UI modules</li>
<li>Creating a consistent layout and navigation system</li>
<li>Implementing individual utility components</li>
<li>Ensuring responsive design with Tailwind</li>
<li>Optimizing for performance</li>
</ol>
<p>The entire project required no backend infrastructure, making deployment simple through static site hosting.
The site is hosted on Cloudflare Pages.</p>
<h2 id="looking-ahead">Looking Ahead</h2>
<p>Util.techformist.com is just getting started. Future plans include:</p>
<ul>
<li>Adding more utility tools based on user feedback!</li>
<li>Implementing more advanced features like tool chaining :)</li>
<li>Improving keyboard shortcuts and accessibility</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Util.techformist.com represents my philosophy that web tools should be fast, private, and straightforward. By keeping everything in the browser, I&rsquo;ve created a resource that respects your data while providing genuinely useful functionality.</p>
<p>Give it a try at <a href="https://utils.techformist.com">utils.techformist.com</a> and let me know what you think.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create a Client-Side PDF Generator with Nuxt 3</title>
      <link>https://techformist.com/create-pdf-json-nuxt/</link>
      <pubDate>Sat, 01 Mar 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/create-pdf-json-nuxt/</guid>
      <description>&lt;p&gt;We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -&lt;/p&gt;
&lt;p&gt;Features will include -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔍 Create/pass a template in HTML and placeholders for values&lt;/li&gt;
&lt;li&gt;🕶️ Real-time document analysis status&lt;/li&gt;
&lt;li&gt;💾 Download merged content as PDF&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a quick demo.
&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2025/docgen-pdf-generator-json-nuxt.gif&#34; type=&#34;&#34; alt=&#34;docgen-pdf-generator-json-nuxt&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;While the demo showcases invoice generation, the same program can be extended to any document type - filled application forms, documents, etc.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -</p>
<p>Features will include -</p>
<ul>
<li>🔍 Create/pass a template in HTML and placeholders for values</li>
<li>🕶️ Real-time document analysis status</li>
<li>💾 Download merged content as PDF</li>
</ul>
<p>Here&rsquo;s a quick demo.
<img loading="lazy" src="/2025/docgen-pdf-generator-json-nuxt.gif" type="" alt="docgen-pdf-generator-json-nuxt"  /></p>
<p>While the demo showcases invoice generation, the same program can be extended to any document type - filled application forms, documents, etc.</p>
<p>Technology stack -</p>
<ol>
<li>NuxtJS</li>
<li>Tailwind CSS</li>
<li>PDFJS</li>
<li>CKEditor</li>
</ol>
<h2 id="why-client-side-pdf-generation">Why Client-Side PDF Generation?</h2>
<p>Generating PDFs on the client-side offers several benefits:</p>
<ul>
<li><strong>Performance:</strong> Offloading PDF generation to the client&rsquo;s device reduces server load and leads to faster response times.</li>
<li><strong>User Experience:</strong> Users can preview changes in real-time, adjust their inputs, and generate PDFs without a trip to the server.</li>
<li><strong>Cost-Effective:</strong> Avoid additional server infrastructure by leveraging modern browsers and their capabilities.</li>
<li><strong>Flexibility:</strong> Easily integrate rich text editing and custom templates to create personalized documents.</li>
</ul>
<p>Who is this post for?</p>
<ul>
<li>You are trying to roll-out a PDF generator for invoices or filled applications</li>
<li>You are using an external service to generate PDFs</li>
<li>You are not satisfied with server-side PDF generators</li>
</ul>
<h2 id="setting-up-the-project">Setting Up the Project</h2>
<p>Before we start, make sure you have Node.js 18.x or later installed in your computer.</p>
<p>Create a blank Nuxt app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bunx nuxi@latest init docgen
</span></span></code></pre></div><p>A key component of our PDF generator is a rich text editor to create templates. We will use CKEditor to achieve just that. We will also use a JSON editor to enable editing JSON data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun i ckeditor/ckeditor5-vue ckeditor5 vue3-json-editor
</span></span></code></pre></div><p>We will use pdfjs to preview and generate PDF, and DomPurify to make sure we are rendering things safer. Also, install types while at it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun i dompurify html2canvas jspdf pdfjs-dist pdfmake
</span></span><span class="line"><span class="cl">bun i -d @types/pdfmake
</span></span></code></pre></div><p>Add styling and icons.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bunx nuxi@latest module add tailwindcss lucide-vue-next
</span></span></code></pre></div><h2 id="develop-the-app">Develop the app</h2>
<h3 id="rich-text-editor">Rich Text Editor</h3>
<p>Rich text editor is one of the main components that enables users to edit templates.
You will not need the rich text editor if you don&rsquo;t plan to edit templates within the app (the template is plain HTML anyway.)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;richtext-editor&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ckeditor</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;editor &amp;&amp; config &amp;&amp; modelValue&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="nt">:editor</span><span class="o">=</span><span class="s">&#34;editor&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;modelValue&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="nt">:config</span><span class="o">=</span><span class="s">&#34;config&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;previewContent&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;preview-overlay prose&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-html</span><span class="o">=</span><span class="s">&#34;previewContent&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">ref</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">ClassicEditor</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Autoformat</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">AutoImage</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Autosave</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Base64UploadAdapter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Bold</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Essentials</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// other items - see GitHub link
</span></span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;ckeditor5&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;ckeditor5/ckeditor5.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Ckeditor</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@ckeditor/ckeditor5-vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;ckeditor5/ckeditor5.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">isLayoutReady</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">editor</span> <span class="o">=</span> <span class="nx">ClassicEditor</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">LICENSE_KEY</span> <span class="o">=</span> <span class="s2">&#34;GPL&#34;</span><span class="p">;</span> <span class="c1">// or &lt;YOUR_LICENSE_KEY&gt;.
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">computed</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isLayoutReady</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">toolbar</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">items</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;sourceEditing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;|&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;heading&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;|&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;fontSize&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;fontFamily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// other toolbar items. See GitHub link for all buttons
</span></span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">shouldNotGroupWhenFull</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// relevant plugins - see GitHub link
</span></span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fontFamily</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">supportAllValues</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fontSize</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s2">&#34;default&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">22</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">supportAllValues</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">heading</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">image</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">toolbar</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;imageTextAlternative&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">initialData</span><span class="o">:</span> <span class="s2">&#34;..&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">licenseKey</span><span class="o">:</span> <span class="nx">LICENSE_KEY</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">link</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">addTargetToExternalLinks</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">defaultProtocol</span><span class="o">:</span> <span class="s2">&#34;https://&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">decorators</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">toggleDownloadable</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;manual&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;Downloadable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">attributes</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">download</span><span class="o">:</span> <span class="s2">&#34;file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">placeholder</span><span class="o">:</span> <span class="s2">&#34;Type or paste your content here!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">table</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">contentToolbar</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableColumn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableRow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;mergeTableCells&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableProperties&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableCellProperties&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">modelValue</span> <span class="o">=</span> <span class="nx">defineModel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">previewContent</span><span class="o">?:</span> <span class="nx">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isLayoutReady</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* include styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="json-editor">JSON Editor</h3>
<p>Create the JSON editor that is used to edit JSON data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">components</span><span class="o">/</span><span class="nx">DataEditor</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[300px] border border-white/20 rounded-lg overflow-hidden bg-white/5&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">textarea</span>
</span></span><span class="line"><span class="cl">        <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;jsonString&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-full h-full p-4 font-mono text-sm bg-transparent text-black dark:text-white/90 focus:outline-none&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">@input</span><span class="s">=&#34;handleInput&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">        <span class="na">spellcheck</span><span class="o">=</span><span class="s">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">textarea</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;error&#34; class=&#34;mt-2 text-red-400 text-sm&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{{</span> <span class="na">error</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">watch</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useDocumentStore</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;~/stores/document&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span> <span class="nx">modelValue</span><span class="o">:</span> <span class="nx">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;</span> <span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emit</span> <span class="o">=</span> <span class="nx">defineEmits</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;update:modelValue&#34;</span><span class="o">:</span> <span class="p">[</span><span class="nx">value</span><span class="o">:</span> <span class="nx">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">documentStore</span> <span class="o">=</span> <span class="nx">useDocumentStore</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">error</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">jsonString</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">modelValue</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">modelValue</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nx">newVal</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">newString</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">newVal</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">jsonString</span><span class="p">.</span><span class="nx">value</span> <span class="o">!==</span> <span class="nx">newString</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">jsonString</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">newString</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleInput</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">Event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="nx">as</span> <span class="nx">HTMLTextAreaElement</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">parsed</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">documentStore</span><span class="p">.</span><span class="nx">isDirty</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;update:modelValue&#34;</span><span class="p">,</span> <span class="nx">parsed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Invalid JSON format&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* include styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="create-pdf-viewer">Create PDF Viewer</h3>
<p>PDF viewer shows the PDF after merging data in template.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">components</span><span class="o">/</span><span class="nx">PdfViewer</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pdf-viewer&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;loading&#34; class=&#34;loading-indicator&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;spinner&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Generating</span> <span class="nx">PDF</span> <span class="nx">preview</span><span class="p">...&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-else-if</span><span class="o">=</span><span class="s">&#34;error&#34; class=&#34;error-message&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{{</span> <span class="na">error</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-else</span><span class="p"> </span><span class="na">ref</span><span class="o">=</span><span class="s">&#34;canvasContainer&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;canvas-container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">watch</span><span class="p">,</span> <span class="nx">onBeforeUnmount</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">content</span><span class="o">:</span> <span class="nx">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">canvasContainer</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">HTMLElement</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">loading</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">error</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pdfDoc</span><span class="o">:</span> <span class="nx">any</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">currentPage</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pageNumPending</span><span class="o">:</span> <span class="nx">number</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pdfjsLib</span><span class="o">:</span> <span class="nx">any</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Clean up any resources when component is unmounted
</span></span></span><span class="line"><span class="cl"><span class="nx">onBeforeUnmount</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">pdfDoc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pdfDoc</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Initialize PDF.js
</span></span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(</span><span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// Import PDF.js dynamically
</span></span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">pdfjsModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfjs-dist&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pdfjsLib</span> <span class="o">=</span> <span class="nx">pdfjsModule</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Set worker source - using CDN for the worker
</span></span></span><span class="line"><span class="cl">      <span class="nx">pdfjsLib</span><span class="p">.</span><span class="nx">GlobalWorkerOptions</span><span class="p">.</span><span class="nx">workerSrc</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Generate and render PDF
</span></span></span><span class="line"><span class="cl">      <span class="nx">await</span> <span class="nx">generatePdf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error initializing PDF.js:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to initialize PDF viewer. Please try again.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Watch for content changes
</span></span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">content</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">pdfjsLib</span> <span class="o">&amp;&amp;</span> <span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">await</span> <span class="nx">generatePdf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Generate PDF from content
</span></span></span><span class="line"><span class="cl"><span class="nx">async</span> <span class="kd">function</span> <span class="nx">generatePdf</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfjsLib</span> <span class="o">||</span> <span class="o">!</span><span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Import pdfMake dynamically
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfMakeModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfmake/build/pdfmake&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfFontsModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfmake/build/vfs_fonts&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Get the default export or the module itself
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfMake</span> <span class="o">=</span> <span class="nx">pdfMakeModule</span><span class="p">.</span><span class="k">default</span> <span class="o">||</span> <span class="nx">pdfMakeModule</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Manually set up the vfs with a basic font
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfMake</span><span class="p">.</span><span class="nx">vfs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// @ts-ignore - We know this property might exist
</span></span></span><span class="line"><span class="cl">      <span class="nx">pdfMake</span><span class="p">.</span><span class="nx">vfs</span> <span class="o">=</span> <span class="nx">pdfFontsModule</span><span class="p">.</span><span class="nx">pdfMake</span><span class="o">?</span><span class="p">.</span><span class="nx">vfs</span> <span class="o">||</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Create a temporary element to parse the HTML
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tempDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;div&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tempDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">content</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Extract text content and structure
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfContent</span> <span class="o">=</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">tempDiv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Define the document with basic fonts
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">docDefinition</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">content</span><span class="o">:</span> <span class="nx">pdfContent</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">defaultStyle</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">font</span><span class="o">:</span> <span class="s2">&#34;Roboto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">12</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">lineHeight</span><span class="o">:</span> <span class="mf">1.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageSize</span><span class="o">:</span> <span class="s2">&#34;A4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageMargins</span><span class="o">:</span> <span class="p">[</span><span class="mi">40</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">60</span><span class="p">]</span> <span class="nx">as</span> <span class="p">[</span><span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Generate PDF as Blob
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfDocGenerator</span> <span class="o">=</span> <span class="nx">pdfMake</span><span class="p">.</span><span class="nx">createPdf</span><span class="p">(</span><span class="nx">docDefinition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">pdfDocGenerator</span><span class="p">.</span><span class="nx">getBlob</span><span class="p">(</span><span class="nx">async</span> <span class="p">(</span><span class="nx">blob</span><span class="o">:</span> <span class="nx">Blob</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Convert blob to array buffer
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">arrayBuffer</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">blob</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Load PDF document
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">loadingTask</span> <span class="o">=</span> <span class="nx">pdfjsLib</span><span class="p">.</span><span class="nx">getDocument</span><span class="p">({</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">arrayBuffer</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pdfDoc</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">loadingTask</span><span class="p">.</span><span class="nx">promise</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Initial/first page rendering
</span></span></span><span class="line"><span class="cl">        <span class="nx">await</span> <span class="nx">renderPage</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error rendering PDF:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to render PDF preview.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error generating PDF:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to generate PDF preview.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Render the specified page
</span></span></span><span class="line"><span class="cl"><span class="nx">async</span> <span class="kd">function</span> <span class="nx">renderPage</span><span class="p">(</span><span class="nx">num</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfDoc</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Get page
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">page</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">pdfDoc</span><span class="p">.</span><span class="nx">getPage</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Clear previous content
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Calculate scale to fit the container width
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">containerWidth</span> <span class="o">=</span> <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="o">?</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">||</span> <span class="mi">800</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">viewport</span> <span class="o">=</span> <span class="nx">page</span><span class="p">.</span><span class="nx">getViewport</span><span class="p">({</span> <span class="nx">scale</span><span class="o">:</span> <span class="mi">1</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">scale</span> <span class="o">=</span> <span class="nx">containerWidth</span> <span class="o">/</span> <span class="nx">viewport</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">scaledViewport</span> <span class="o">=</span> <span class="nx">page</span><span class="p">.</span><span class="nx">getViewport</span><span class="p">({</span> <span class="nx">scale</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Create canvas for each page
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s2">&#34;2d&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">scaledViewport</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">scaledViewport</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s2">&#34;pdf-page&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Add canvas to container
</span></span></span><span class="line"><span class="cl">    <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="o">?</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Render PDF page into canvas context
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">renderContext</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">canvasContext</span><span class="o">:</span> <span class="nx">ctx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">viewport</span><span class="o">:</span> <span class="nx">scaledViewport</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">renderContext</span><span class="p">).</span><span class="nx">promise</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// If there&#39;s a pending page, render it
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">pageNumPending</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">renderPage</span><span class="p">(</span><span class="nx">pageNumPending</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageNumPending</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// If there are more pages, render them too
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">num</span> <span class="o">&lt;</span> <span class="nx">pdfDoc</span><span class="p">.</span><span class="nx">numPages</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">renderPage</span><span class="p">(</span><span class="nx">num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error rendering page:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to render PDF page.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Extract content from HTML for PDF generation
</span></span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">element</span><span class="o">:</span> <span class="nx">HTMLElement</span><span class="p">)</span><span class="o">:</span> <span class="nx">any</span><span class="p">[]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span><span class="o">:</span> <span class="nx">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Process child nodes
</span></span></span><span class="line"><span class="cl">  <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">childNodes</span><span class="p">).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">node</span><span class="o">:</span> <span class="nx">Node</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">nodeType</span> <span class="o">===</span> <span class="nx">Node</span><span class="p">.</span><span class="nx">TEXT_NODE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">text</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">text</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> <span class="nx">text</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">nodeType</span> <span class="o">===</span> <span class="nx">Node</span><span class="p">.</span><span class="nx">ELEMENT_NODE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">elementNode</span> <span class="o">=</span> <span class="nx">node</span> <span class="nx">as</span> <span class="nx">HTMLElement</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">nodeName</span> <span class="o">=</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Handle different element types
</span></span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;p&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h1&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">24</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h2&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">20</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h3&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">16</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ul&#34;</span> <span class="o">||</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ol&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">items</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">.</span><span class="nx">children</span><span class="p">).</span><span class="nx">map</span><span class="p">((</span><span class="nx">li</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="nx">li</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">ul</span><span class="o">:</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ul&#34;</span> <span class="o">?</span> <span class="nx">items</span> <span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">ol</span><span class="o">:</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ol&#34;</span> <span class="o">?</span> <span class="nx">items</span> <span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;table&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Extract table data
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">tableData</span><span class="o">:</span> <span class="nx">string</span><span class="p">[][]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;tr&#34;</span><span class="p">)).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">row</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">rowData</span><span class="o">:</span> <span class="nx">string</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">          <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">row</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;td, th&#34;</span><span class="p">)).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">cell</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">rowData</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">cell</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">rowData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">tableData</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">rowData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">tableData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">table</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nx">body</span><span class="o">:</span> <span class="nx">tableData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nx">widths</span><span class="o">:</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">tableData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">length</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="s2">&#34;*&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Recursively process other elements
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">childContent</span> <span class="o">=</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">childContent</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(...</span><span class="nx">childContent</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Include this in a <code>DocumentPreview.vue</code> component that can toggle between PDF viewer and a rich text viewer - if needed, of course.</p>
<h3 id="layout-and-page">Layout and Page</h3>
<p>Update layout -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">layouts</span><span class="o">/</span><span class="k">default</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;min-h-screen bg-surface-ground&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">nav</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;bg-surface-card border-b border-surface-border md:px-12 px-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34; h-16 flex items-center justify-between&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-xl font-bold text-black&#34;</span><span class="p">&gt;</span><span class="nx">docgen</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex items-center gap-4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">            <span class="na">variant</span><span class="o">=</span><span class="s">&#34;secondary&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="o">:</span><span class="na">icon</span><span class="o">=</span><span class="s">&#34;isDark ? &#39;sun&#39; : &#39;moon&#39;&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">@click</span><span class="s">=&#34;toggleTheme&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">            <span class="o">:</span><span class="na">title</span><span class="o">=</span><span class="s">&#34;isDark ? &#39;Switch to light mode&#39; : &#39;Switch to dark mode&#39;&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">main</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34; py-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">slot</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">main</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>&hellip; and the page.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mx-auto px-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grid grid-cols-1 lg:grid-cols-2 gap-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col gap-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Template</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[600px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">RichTextEditor</span> <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;template&#34; class=&#34;h-full&#34;</span><span class="p"> /&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="c">&lt;!--</span> <span class="nx">Removed</span> <span class="nx">flex</span><span class="o">-</span><span class="mi">1</span> <span class="nx">from</span> <span class="nx">card</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Data</span> <span class="p">(</span><span class="nx">JSON</span><span class="p">)&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;editor-container h-[400px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">DataEditor</span> <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;jsonData&#34; class=&#34;h-full&#34;</span><span class="p"> /&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="c">&lt;!--</span> <span class="nx">Removed</span> <span class="nx">flex</span><span class="o">-</span><span class="mi">1</span> <span class="nx">from</span> <span class="nx">card</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Generated</span> <span class="nx">Document</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar-actions&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="c">&lt;!--</span> <span class="p">&lt;</span><span class="nt">Button</span> <span class="na">variant</span><span class="o">=</span><span class="s">&#34;secondary&#34;</span> <span class="na">icon</span><span class="o">=</span><span class="s">&#34;refresh&#34;</span><span class="p">&gt;</span><span class="nx">Auto</span><span class="o">-</span><span class="nx">refresh</span><span class="p">&lt;/</span><span class="nt">Button</span><span class="p">&gt;</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[1024px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">DocumentPreview</span> <span class="nt">:data</span><span class="o">=</span><span class="s">&#34;jsonData&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-full&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useDocumentStore</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;~/stores/document&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">storeToRefs</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;pinia&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">documentStore</span> <span class="o">=</span> <span class="nx">useDocumentStore</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">template</span><span class="p">,</span> <span class="nx">jsonData</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">storeToRefs</span><span class="p">(</span><span class="nx">documentStore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="run-the-app">Run the app!</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun dev
</span></span></code></pre></div><p>This should populate some default template and data, and show you a nice preview of the merged content.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generating PDF on client is not hard and keeps your server load low.</p>
<p>While generating PDFs client-side can certainly help in most use cases (especially for enterprise customers), it may not be a pleasant experience for mobile users. Also, features like emailing PDF, supporting non-PDF formats, etc. are hard to achieve.</p>
<p>See complete code on <a href="http://github.com/prashanth1k/docgen">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Document Analysis App with Azure Document Intelligence</title>
      <link>https://techformist.com/document-analysis-app-nuxt/</link>
      <pubDate>Sun, 23 Feb 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/document-analysis-app-nuxt/</guid>
      <description>&lt;p&gt;We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;upload PDF or images&lt;/li&gt;
&lt;li&gt;specify fields (if needed)&lt;/li&gt;
&lt;li&gt;extract specific fields of information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Features will include -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔍 Extract custom fields from identification documents&lt;/li&gt;
&lt;li&gt;🔄 Real-time document analysis status&lt;/li&gt;
&lt;li&gt;💾 Download extracted data as JSON&lt;/li&gt;
&lt;li&gt;🎨 UI with Shadcn-vue /Tailwind CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a quick demo.
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/_FLmnAfiqUo?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;
&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -</p>
<ul>
<li>upload PDF or images</li>
<li>specify fields (if needed)</li>
<li>extract specific fields of information</li>
</ul>
<p>Features will include -</p>
<ul>
<li>🔍 Extract custom fields from identification documents</li>
<li>🔄 Real-time document analysis status</li>
<li>💾 Download extracted data as JSON</li>
<li>🎨 UI with Shadcn-vue /Tailwind CSS</li>
</ul>
<p>Here&rsquo;s a quick demo.
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_FLmnAfiqUo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>
</p>
<p>While the demo covers only identification documents, the same program can be extended to any document type - application forms, invoices, etc.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have the following:</p>
<ul>
<li>Node.js 18.x or later</li>
<li>An Azure account with a Document Intelligence resource</li>
<li>Azure Document Intelligence API key and endpoint</li>
</ul>
<h2 id="step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Create a blank Nuxt app..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx nuxi@latest init docformist
</span></span></code></pre></div><p>Get your API key and environment details from <a href="https://azure.microsoft.com/products/ai-services/ai-document-intelligence">Azure site</a>. You can sign up for free and use a Free plan to test out the service.</p>
<p>Create a <code>.env</code> file in the root directory with your Azure credentials:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-env" data-lang="env"><span class="line"><span class="cl"><span class="nv">AZURE_DOCUMENT_INTELLIGENCE_KEY</span><span class="o">=</span>your_key_here
</span></span><span class="line"><span class="cl"><span class="nv">AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT</span><span class="o">=</span>your_endpoint_here
</span></span></code></pre></div><p>Add Nuxt modules -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx nuxi@latest module add shadcn-nuxt
</span></span><span class="line"><span class="cl">pnpx nuxi@latest module add tailwindcss
</span></span></code></pre></div><p>Initialize shadcn and tailwind -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx tailwindcss init
</span></span><span class="line"><span class="cl">pnpx shadcn-vue@latest init
</span></span></code></pre></div><p>Accept sensible defaults.</p>
<h2 id="step-2-configuring-nuxt">Step 2: Configuring Nuxt</h2>
<p>Ensure <code>nuxt.config.ts</code> is updated. Add the CSS file reference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineNuxtConfig</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;shadcn-nuxt&#34;</span><span class="p">,</span> <span class="s2">&#34;@nuxt/icon&#34;</span><span class="p">,</span> <span class="s2">&#34;@nuxtjs/tailwindcss&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">css</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;~/assets/css/tailwind.css&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Add shadcn-vue components.</p>
<pre tabindex="0"><code>pnpx shadcn-vue@latest add label button card input separator
</code></pre><h2 id="step-3-creating-the-document-uploader-component">Step 3: Creating the Document Uploader Component</h2>
<p>Create <code>DocumentUploader.vue</code> in the components folder to handle file uploads:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/components/DocumentUploader.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emit</span> <span class="o">=</span> <span class="nx">defineEmits</span><span class="p">([</span><span class="s2">&#34;file-selected&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleFileSelect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">Event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">file</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="nx">as</span> <span class="nx">HTMLInputElement</span><span class="p">).</span><span class="nx">files</span><span class="o">?</span><span class="p">.[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;file-selected&#34;</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleDrop</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">DragEvent</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">file</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">dataTransfer</span><span class="o">?</span><span class="p">.</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;file-selected&#34;</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-4-creating-the-document-viewer-component">Step 4: Creating the Document Viewer Component</h2>
<p>Create <code>DocumentViewer.vue</code> to preview the uploaded document:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/components/DocumentViewer.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fileUrl</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">isPDF</span> <span class="o">=</span> <span class="nx">computed</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="s2">&#34;application/pdf&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">file</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nx">newFile</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">URL</span><span class="p">.</span><span class="nx">revokeObjectURL</span><span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">newFile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">immediate</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">onBeforeUnmount</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">URL</span><span class="p">.</span><span class="nx">revokeObjectURL</span><span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-5-creating-the-api-endpoint">Step 5: Creating the API Endpoint</h2>
<p>Create an API endpoint <code>analyze.post.ts</code> for document analysis with Azure Document Intelligence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">DocumentIntelligence</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">getLongRunningPoller</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isUnexpected</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@azure-rest/ai-document-intelligence&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">AzureKeyCredential</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@azure/core-auth&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">formidable</span> <span class="kr">from</span> <span class="s2">&#34;formidable&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">readFile</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;fs/promises&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineEventHandler</span><span class="p">(</span><span class="kr">async</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h2 id="step-6-building-the-main-page">Step 6: Building the Main Page</h2>
<p>Integrate the components and API in the main page <code>index.vue</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/pages/index.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">selectedFile</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">File</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">extractedData</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;</span> <span class="o">|</span> <span class="kc">null</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">loading</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">selectedFields</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">[]</span><span class="p">&gt;([]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleFileSelect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">selectedFile</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">extractedData</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleExtract</span> <span class="o">=</span> <span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">selectedFields</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;No fields selected&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">formData</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FormData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;file&#34;</span><span class="p">,</span> <span class="nx">selectedFile</span><span class="p">.</span><span class="nx">value</span><span class="o">!</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;fields&#34;</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">selectedFields</span><span class="p">.</span><span class="nx">value</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;/api/analyze&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">method</span><span class="o">:</span> <span class="s2">&#34;POST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">body</span><span class="o">:</span> <span class="nx">formData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;Failed to analyze document&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">extractedData</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error analyzing document:&#34;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// TODO: Show error toast
</span></span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-7-run-the-application">Step 7: Run the Application</h2>
<p>Start the development server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pnpm dev
</span></span></code></pre></div><p>Visit <code>http://localhost:3000</code> to upload a document and see the results.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That was a quick demo of how we build a document analysis / identification app using Azure Document Intelligence. Customize the application further to meet your needs.</p>
<p>Here&rsquo;s the Github repo - <a href="https://github.com/prashanth1k/docformist">https://github.com/prashanth1k/docformist</a>.</p>
<p>All of this was done within 2 hours - thanks to AI. The era of personal applications is here.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Teleport in VueJS</title>
      <link>https://techformist.com/teleport-vuejs/</link>
      <pubDate>Wed, 20 Jan 2021 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/teleport-vuejs/</guid>
      <description>&lt;p&gt;Teleport is a new feature introduced in Vue 3. Teleport provides better control to developers on where exactly an element is rendered.&lt;/p&gt;
&lt;h2 id=&#34;get-teleporting&#34;&gt;Get Teleporting&lt;/h2&gt;
&lt;p&gt;Let us create a new Vue 3 app to start playing around with teleport. We will use Vite, because it is 2021.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmd&#34; data-lang=&#34;cmd&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm init @vitejs/app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Provide a project name (&lt;code&gt;teleport&lt;/code&gt;) and select &lt;code&gt;vue&lt;/code&gt; as the template.&lt;/p&gt;
&lt;p&gt;Install dependencies and start the app.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmd&#34; data-lang=&#34;cmd&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;cd&lt;/span&gt; teleport
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm i
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; to see your new app.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Teleport is a new feature introduced in Vue 3. Teleport provides better control to developers on where exactly an element is rendered.</p>
<h2 id="get-teleporting">Get Teleporting</h2>
<p>Let us create a new Vue 3 app to start playing around with teleport. We will use Vite, because it is 2021.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm init @vitejs/app
</span></span></code></pre></div><p>Provide a project name (<code>teleport</code>) and select <code>vue</code> as the template.</p>
<p>Install dependencies and start the app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">cd</span> teleport
</span></span><span class="line"><span class="cl">npm i
</span></span><span class="line"><span class="cl">npm run dev
</span></span></code></pre></div><p>Navigate to <code>http://localhost:3000</code> to see your new app.</p>
<p>Replace the <code>&lt;template&gt;</code> section in <code>src/components/HelloWorld.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>{{ msg }}<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Create a new component <code>src/components/Messages.vue</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hi&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Goodbye&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">HelloWorld</span> <span class="nx">from</span> <span class="s2">&#34;./HelloWorld.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Replace <code>&lt;template&gt;</code> section in <code>src/App.vue</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">Messages</span><span class="p">&gt;&lt;/</span><span class="nt">Messages</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;height: 100px&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>We are just rearranging a couple of things and setting the stage for demonstrating teleport.</p>
<p>You will see output as expected -</p>
<p><img loading="lazy" src="/2021/vue-simple-app-no-teleport.jpg" type="" alt="vue-simple-app-no-teleport"  /></p>
<p>This makes absolute sense -</p>
<ol>
<li>You create a component and build up UI with components</li>
<li>You use the components in the parent component/view exactly in the place you need it</li>
</ol>
<p>Teleport helps us to write code in a logical fashion but have more control over where the component is rendered.</p>
<p>For e.g., we can change the code in <code>Messages.vue</code> to -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hello&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Hi&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">teleport</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;#startapp&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Goodbye&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">HelloWorld</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">teleport</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Now, change <code>App.vue</code> to -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;startapp&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">Messages</span><span class="p">&gt;&lt;/</span><span class="nt">Messages</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./assets/logo.png&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;height: 100px&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The third message is moved to the first position, i.e, the rendering of the component will be teleported to the tag with id <code>startapp</code>.</p>
<p>You may in fact transport anything outside of the entire Vue render tree. For e.g., change <code>index.html</code> to -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;icon&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/favicon.ico&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width, initial-scale=1.0&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Vite App<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;startapp&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;module&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/src/main.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p><img loading="lazy" src="/2021/vue-simple-app-teleport.jpg" type="" alt="vue-simple-app-teleport"  /></p>
<h2 id="where-to-use-teleport">Where to use teleport?</h2>
<p>Teleport offers a cleaner segregation from the &ldquo;coding&rdquo; side of things as compared to where a component/element is rendered.</p>
<p>Here are a couple of use cases where teleport can help-</p>
<ul>
<li>High degree of control over positioning of elements: Display elements like status bar always at the bottom of page - no matter where they are used. You are not constrained to code in those components at the very end</li>
<li>Modals: You can have a conditional display of modal dialog anywhere in the app, but you may want to move it to end. Or, you may move a full-screen modal to the <code>body</code> tag and have a cleaner UI</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create Reddit Reader Using Vue 3 &amp; Vite</title>
      <link>https://techformist.com/reddit-reader-vue3-vite/</link>
      <pubDate>Wed, 30 Dec 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/reddit-reader-vue3-vite/</guid>
      <description>&lt;p&gt;Hello everyone! Hope you are all set for the new year. While you are waiting for the Y2020 to end with bated breath, here&amp;rsquo;s a post to kick start your Vue 3 journey. We will create a Reddit reader using Vue3 and Vite!&lt;/p&gt;
&lt;p&gt;This post is more useful for someone with basic knowledge of Vue and Vue 2.&lt;/p&gt;
&lt;h2 id=&#34;get-started-installation&#34;&gt;Get Started: Installation&lt;/h2&gt;
&lt;p&gt;Create a Vue 3 project with Vite..&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmd&#34; data-lang=&#34;cmd&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm create @vitejs/app
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Select the &lt;code&gt;vue&lt;/code&gt; template on prompt to create a new folder for your project. Install dependencies.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Hello everyone! Hope you are all set for the new year. While you are waiting for the Y2020 to end with bated breath, here&rsquo;s a post to kick start your Vue 3 journey. We will create a Reddit reader using Vue3 and Vite!</p>
<p>This post is more useful for someone with basic knowledge of Vue and Vue 2.</p>
<h2 id="get-started-installation">Get Started: Installation</h2>
<p>Create a Vue 3 project with Vite..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm create @vitejs/app
</span></span></code></pre></div><p>Select the <code>vue</code> template on prompt to create a new folder for your project. Install dependencies.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">cd</span> reddit-reader-vite-vue-app
</span></span><span class="line"><span class="cl">npm i
</span></span></code></pre></div><p>Run the project -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm run dev
</span></span></code></pre></div><p>The project starts almost instantaneously.. sweet. Do two more things -</p>
<ul>
<li>Open the project folder in VSCode</li>
<li>Open <code>http://localhost:3000/</code> in your browser</li>
</ul>
<p>Edit the file <code>index.html</code> and include reference to a light-weight CSS framework. I use <a href="https://jenil.github.io/chota/">chota css</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/chota@latest&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>Create a blank file at <code>src/assets/css/styles.css</code> to create custom styles (we will include this file in <code>main.js</code>). I will not explicitly outline any custom styles here, but you should find them here on the Github repo.</p>
<p>Yay.. you have this beautiful app.</p>
<p><img loading="lazy" src="/2020/vite3-initial-page.jpg" type="" alt="vite3-initial-page"  /></p>
<p>You are all set! Let us start coding in the app.</p>
<h2 id="setup-vue-router">Setup Vue Router</h2>
<p>We will have a bunch of links (well, two to be exact) to navigate within the app. Vue Router makes that routing real easy. Let&rsquo;s set it up.</p>
<p>First, install Vue router compatible with Vue3.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i vue-router@4
</span></span></code></pre></div><p>Next, create the router file <code>src/router/index.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createWebHistory</span><span class="p">,</span> <span class="nx">createRouter</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue-router&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Home</span> <span class="nx">from</span> <span class="s2">&#34;../views/Home.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">history</span> <span class="o">=</span> <span class="nx">createWebHistory</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="nx">component</span><span class="o">:</span> <span class="nx">Home</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/top&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Posts.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span> <span class="nx">filter</span><span class="o">:</span> <span class="s2">&#34;top&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/controversial&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Posts.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span> <span class="nx">filter</span><span class="o">:</span> <span class="s2">&#34;controversial&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> <span class="nx">history</span><span class="p">,</span> <span class="nx">routes</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">router</span><span class="p">;</span>
</span></span></code></pre></div><p>We will use the same view <code>Posts</code> for both top and controversial posts. The view will decide which data to fetch based on the prop called <code>filter</code>, which gets passed by the router.</p>
<p>Change <code>src/main.js</code> to use the router file and include the custom style file we had created earlier -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">createApp</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="nx">from</span> <span class="s2">&#34;./App.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">router</span> <span class="nx">from</span> <span class="s2">&#34;./router&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./assets/css/styles.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">createApp</span><span class="p">(</span><span class="nx">App</span><span class="p">).</span><span class="nx">use</span><span class="p">(</span><span class="nx">router</span><span class="p">).</span><span class="nx">mount</span><span class="p">(</span><span class="s2">&#34;#app&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>There&rsquo;s a small problem with the router code - we don&rsquo;t have those views yet. Let&rsquo;s do that now.</p>
<h2 id="setup-views">Setup Views</h2>
<p>We will create two new views and setup <code>App</code> to use the router.</p>
<h4 id="home">Home</h4>
<p>Create a new view for the home page - <code>src/views/Home.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h3</span><span class="p">&gt;</span>Reddit Reader<span class="p">&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>This is a simple Reddit Reader app.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card hero justify-text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;is-center text-light&#34;</span><span class="p">&gt;</span>Start Here!<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/top&#34;</span><span class="p">&gt;</span>Top Posts<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="ni">&amp;nbsp;</span> | <span class="ni">&amp;nbsp;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/controversial&#34;</span><span class="p">&gt;</span>Controversial Posts<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><h4 id="posts">Posts</h4>
<p>Create a new view for showing the list of posts - <code>src/views/Posts.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h4</span><span class="p">&gt;</span>Posts<span class="p">&lt;/</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h4 id="appvue">App.vue</h4>
<p>Change code in <code>src/App.vue</code> to make use of router.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">header</span><span class="p">&gt;&lt;/</span><span class="nt">header</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">router-view</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;$route.path&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">footer</span><span class="p">&gt;&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">Header</span> <span class="nx">from</span> <span class="s2">&#34;./components/Header.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">Footer</span> <span class="nx">from</span> <span class="s2">&#34;./components/Footer.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">#</span><span class="nn">app</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="n">Avenir</span><span class="p">,</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span><span class="p">,</span> <span class="kc">sans-serif</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kp">-webkit-</span><span class="n">font-smoothing</span><span class="p">:</span> <span class="n">antialiased</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kp">-moz-</span><span class="n">osx-font-smoothing</span><span class="p">:</span> <span class="n">grayscale</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#2c3e50</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">60</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Let&rsquo;s also create two new components for header and footer, which display the logo/brand, navbar and footer for the entire site.</p>
<p>Create new file <code>src/components/Header.vue</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">nav</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav-left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;logo-brand&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Reddit Reader<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav-right&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;tabs&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/top&#34;</span><span class="p">&gt;</span>Top<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/controversial&#34;</span><span class="p">&gt;</span>Controversial<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Create new file <code>src/components/Footer.vue</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">footer</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-grey bg-light&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container is-left&#34;</span><span class="p">&gt;</span>i am groot<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="change-post-view">Change Post View</h2>
<p>Let&rsquo;s go back to our posts view and do just one thing - outline a list of <code>top</code> and <code>controversial</code> posts. No pagination, showing the page details - nothing. We just take the user to Reddit when a link is clicked. We will use the all new Vue composition API and features.</p>
<p>Let&rsquo;s start by extending the template created earlier to receive props from router, call Reddit API, and display the results.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">strong</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-light&#34;</span><span class="p">&gt;</span>posts<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;error-msg&#34;</span><span class="p">&gt;</span>{{ err }}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{ posts }}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">defineProps</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">err</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">mounted</span> <span class="o">=</span> <span class="nx">onMounted</span><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Fetching them posts..&#34;</span><span class="p">,</span> <span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetchPosts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;posts.value: &#34;</span><span class="p">,</span> <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="p">({</span> <span class="nx">filter</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;top&#34;</span> <span class="p">}</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">fetchPosts</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">query</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`https://api.reddit.com/</span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="si">}</span><span class="sb">?limit=10`</span>
</span></span><span class="line"><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">resJson</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">resJson</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">children</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">statusText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;data: &#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Straight off the bat you will notice a few curiosities.</p>
<h6 id="variables">Variables</h6>
<p>There are no distinct sections that will be exported by default. Instead, we make do with a new function called <code>setup()</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This is further simplified by using <code>&lt;script setup&gt;</code> tag.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">defineProps</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h6 id="variables-and-state">Variables and State</h6>
<p>Instead of a <code>data()</code> section in Vue2, we directly define variables in <code>script</code> (you could always mix and match, and confuse yourself and others though).</p>
<p>But, we need to tell Vue that we are creating a state variable that may also be reactive. We do that using <code>ref</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">err</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h6 id="props">Props</h6>
<p>Define props with -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="p">({</span> <span class="nx">filter</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;top&#34;</span> <span class="p">}</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h6 id="life-cycle-hooks">Life Cycle Hooks</h6>
<p>We use <code>onMounted</code> hook to fetch data from Reddit.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">mounted</span> <span class="o">=</span> <span class="nx">onMounted</span><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Fetching them posts..&#34;</span><span class="p">,</span> <span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetchPosts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;posts.value: &#34;</span><span class="p">,</span> <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h6 id="methods">Methods</h6>
<p>Methods can be defined anywhere within <code>script</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">fetchPosts</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">query</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`https://api.reddit.com/</span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="si">}</span><span class="sb">?limit=10`</span>
</span></span><span class="line"><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">resJson</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">resJson</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">children</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">statusText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;data: &#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>With a few cosmetic chahges, the final code for <code>Posts.vue</code> is outlined below -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">strong</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-light&#34;</span><span class="p">&gt;</span>{{ props.filter || &#34;&#34; }} posts<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;error-msg&#34;</span><span class="p">&gt;</span>{{ err }}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;!posts || posts.length &lt;= 0&#34;</span><span class="p">&gt;</span>Loading..<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-item card text-left&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(post, index) in posts&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">v-else</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h3</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-title&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">a</span> <span class="na">:href</span><span class="o">=</span><span class="s">&#34;post?.data?.url&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span><span class="p">&gt;</span>{{ post?.data?.title }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;post-item-meta&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">          <span class="na">:href</span><span class="o">=</span><span class="s">&#34;`https://reddit.com/${post?.data?.subreddit_name_prefixed}`&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">target</span><span class="o">=</span><span class="s">&#34;blank&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>{{ post?.data?.subreddit_name_prefixed }}<span class="p">&lt;/</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        | {{ post?.data?.num_comments }} comments
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">defineProps</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">err</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">mounted</span> <span class="o">=</span> <span class="nx">onMounted</span><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Fetching them posts..&#34;</span><span class="p">,</span> <span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetchPosts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;posts.value: &#34;</span><span class="p">,</span> <span class="nx">posts</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="p">({</span> <span class="nx">filter</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;top&#34;</span> <span class="p">}</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">fetchPosts</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">query</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">fetch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="sb">`https://api.reddit.com/</span><span class="si">${</span><span class="nx">props</span><span class="p">.</span><span class="nx">filter</span><span class="si">}</span><span class="sb">?limit=10`</span>
</span></span><span class="line"><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">resJson</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">resJson</span><span class="o">?</span><span class="p">.</span><span class="nx">data</span><span class="o">?</span><span class="p">.</span><span class="nx">children</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">statusText</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;data: &#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="nx">err</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Your app is now ready to rock!</p>
<p><img loading="lazy" src="/2021/reddit-reader-posts-vite.gif" type="" alt="reddit-reader-posts-vite"  /></p>
<h2 id="finis">Finis</h2>
<p>You will find the complete code at this <a href="https://github.com/prashanth1k/reddit-reader-vite-vue-app">Github repo</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Save div as Image in Vue</title>
      <link>https://techformist.com/save-div-image-vue/</link>
      <pubDate>Wed, 04 Nov 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/save-div-image-vue/</guid>
      <description>&lt;p&gt;We have previously seen how a &lt;a href=&#34;https://techformist.com/print-section-page-button-vue/&#34;&gt;&lt;code&gt;div&lt;/code&gt; element can be saved to a file&lt;/a&gt;. In this post we will see how we can save &lt;code&gt;div&lt;/code&gt; tag contents as an image in Vue.&lt;/p&gt;
&lt;h2 id=&#34;how-do-we-print-the-div&#34;&gt;How do we print the &lt;code&gt;div&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Here are the steps at a high level -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Draw contents of &lt;code&gt;div&lt;/code&gt; to canvas. We use a library called &lt;a href=&#34;https://html2canvas.hertzen.com/&#34;&gt;html2canvas&lt;/a&gt; to create canvas from a specified &lt;code&gt;div&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Generate image from canvas&lt;/li&gt;
&lt;li&gt;Download canvas as an image&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;print&lt;/code&gt; here is equivalent to &amp;ldquo;taking the screenshot&amp;rdquo; of a particular section of your app.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We have previously seen how a <a href="/print-section-page-button-vue/"><code>div</code> element can be saved to a file</a>. In this post we will see how we can save <code>div</code> tag contents as an image in Vue.</p>
<h2 id="how-do-we-print-the-div">How do we print the <code>div</code>?</h2>
<p>Here are the steps at a high level -</p>
<ol>
<li>Draw contents of <code>div</code> to canvas. We use a library called <a href="https://html2canvas.hertzen.com/">html2canvas</a> to create canvas from a specified <code>div</code>.</li>
<li>Generate image from canvas</li>
<li>Download canvas as an image</li>
</ol>
<p>The <code>print</code> here is equivalent to &ldquo;taking the screenshot&rdquo; of a particular section of your app.</p>
<h2 id="code">Code</h2>
<p>The example code is on <a href="https://codepen.io/techformist/pen/poEOpjb">Codepen</a>. Explanation below.</p>
<h4 id="create-html">Create HTML</h4>
<p>Create some html that includes content that needs to be printed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container text-center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">h4</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;&#34;</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>Example to Print div in Vue<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">h4</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;http://techformist.com/save-div-image-javascript&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>Accompanying Blog Post<span class="p">&lt;/</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">button</span> <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">preventDefault</span><span class="o">=</span><span class="s">&#34;printThis&#34;</span><span class="p">&gt;</span>Print!<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-center&#34;</span> <span class="na">ref</span><span class="o">=</span><span class="s">&#34;printcontent&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>Example content to print<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
</span></span><span class="line"><span class="cl">      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
</span></span><span class="line"><span class="cl">      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
</span></span><span class="line"><span class="cl">      commodo consequat.
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">svg</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;100&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;100&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">circle</span>
</span></span><span class="line"><span class="cl">        <span class="na">cx</span><span class="o">=</span><span class="s">&#34;50&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">cy</span><span class="o">=</span><span class="s">&#34;50&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">r</span><span class="o">=</span><span class="s">&#34;40&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">stroke</span><span class="o">=</span><span class="s">&#34;green&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">stroke-width</span><span class="o">=</span><span class="s">&#34;4&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">fill</span><span class="o">=</span><span class="s">&#34;yellow&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">svg</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The objective is to print all content within the <code>div</code> with <code>ref=printcontent</code>.</p>
<h4 id="script">Script</h4>
<p>First, we include reference to the <a href="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.min.js">HTML2Canvas CDN</a>.</p>
<p>Next, create some Vue code -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">printThis</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;printing..&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">el</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">printcontent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">type</span><span class="o">:</span> <span class="s2">&#34;dataURL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">printCanvas</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">html2canvas</span><span class="p">(</span><span class="nx">el</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">,</span> <span class="s2">&#34;download.png&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;href&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">printCanvas</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="s2">&#34;image/png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s2">&#34;image/png&#34;</span><span class="p">,</span> <span class="s2">&#34;image/octet-stream&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">link</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;done&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({}),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>The code is largely self-expanatory -</p>
<ol>
<li>Get reference to the element that needs to be printed - <code>const el = this.$refs.printcontent</code></li>
<li>Generate canvas from element - <code>const printCanvas = await html2canvas(el, options);</code></li>
<li>Convert canvas to an image
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">printCanvas</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="s2">&#34;image/png&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s2">&#34;image/png&#34;</span><span class="p">,</span> <span class="s2">&#34;image/octet-stream&#34;</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>Generate a link that will prompt user to download image when clicked - <code>const link = document.createElement(&quot;a&quot;);</code>. Point the link to the image generated in the previous step</li>
</ol>
<h2 id="end-result">End Result</h2>
<p>That&rsquo;s about it! Click on <code>Print!</code> button to download image.</p>
<p><img loading="lazy" src="/2021/print-div-contents-vue-canvas.gif" type="" alt="print-div-contents-vue-canvas"  /></p>
<p>Note that the image in the content cannot be from another domain since that will taint the canvas (and the image will not be rendered).</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Build a Simple Timesheet App using Vue &amp; Vuetify</title>
      <link>https://techformist.com/build-simple-timesheet-app-vue-vuetify/</link>
      <pubDate>Wed, 28 Oct 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/build-simple-timesheet-app-vue-vuetify/</guid>
      <description>&lt;p&gt;In this post we will see how to create a simple timesheet app using Vue and Vuetify. This is not quite a comprehensive tutorial on Vue or Vuetify, rather a demo of front-end features, see how easy it is to build an usable app, and in general, how modern app development makes the whole process enjoyable.&lt;/p&gt;
&lt;h2 id=&#34;what-are-we-building&#34;&gt;What are we building?&lt;/h2&gt;
&lt;p&gt;A simple timesheet app that will -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enable to enter time on a day&lt;/li&gt;
&lt;li&gt;enable export of time data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will store everything in browser storage and there will be no standard persistence layer. This app may or may not plugin to backend (Firebase? ExpressJS / Fastify? Hasura? Other?) in the future - comment and let me know if you&amp;rsquo;re interested in seeing that!&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this post we will see how to create a simple timesheet app using Vue and Vuetify. This is not quite a comprehensive tutorial on Vue or Vuetify, rather a demo of front-end features, see how easy it is to build an usable app, and in general, how modern app development makes the whole process enjoyable.</p>
<h2 id="what-are-we-building">What are we building?</h2>
<p>A simple timesheet app that will -</p>
<ul>
<li>enable to enter time on a day</li>
<li>enable export of time data</li>
</ul>
<p>We will store everything in browser storage and there will be no standard persistence layer. This app may or may not plugin to backend (Firebase? ExpressJS / Fastify? Hasura? Other?) in the future - comment and let me know if you&rsquo;re interested in seeing that!</p>
<p>Technically, we will focus on -</p>
<ol>
<li>Use Vuex and router (latter is not quite needed, you will see more of this)</li>
<li>Form validations</li>
<li>Make application a joy to use with keyboard shortcuts</li>
</ol>
<p>This is how it looks..</p>
<p><img loading="lazy" src="/2020/simple-timesheet-app-daily-demo.gif" type="" alt="simple-timesheet-app-daily-demo"  /></p>
<p>Go to <a href="https://simple-timesheet.techformist.com">https://simple-timesheet.techformist.com</a> to see how the application works.</p>
<h2 id="get-started-with-vue">Get started with Vue</h2>
<p>If you don&rsquo;t have Vue CLI, this is a good time to go ahead and install it. Vue CLI is the starting point for all great Vue projects.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm install -g @vue/cli
</span></span></code></pre></div><p>Create the project -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">vue create simple-timesheet
</span></span></code></pre></div><p>Select the Vue version 2 for the sake of this project and instruct wizard that you will <strong>Manually select features</strong>. Also, select the below features/options -</p>
<ul>
<li>Options: Babel, PWA Support (optional), Router, Vuex, Linter / Formatter</li>
<li><code>Use history mode</code> to &lsquo;Y&rsquo; (default)</li>
<li>A sensible option for ESLint - I recommend <code>ESLint + Prettier</code></li>
<li>Store configuration in dedicated configuration files</li>
</ul>
<p>Sit back while Vue CLI downloads packages and creates the project.</p>
<p>Once done, add Vuetify to the project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">cd</span> simple-timesheet
</span></span><span class="line"><span class="cl">vue add vuetify
</span></span></code></pre></div><p>Choose the default Vuetify configuration.</p>
<p>Add packages to our app - we will see their role in subsequent sections.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vuex-persist vue-shortkey
</span></span></code></pre></div><p>Your project is ready to roll. Open the project folder in VS Code, the greatest editor of our times to get started.</p>
<h2 id="wire-everything-up">Wire Everything Up</h2>
<p>Vue comes packaged with all the things you need - so, there&rsquo;s no complex wiring involved. Let us focus on getting everything to a baseline version to start creating magic.</p>
<p>Change the default <code>App.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-app-bar</span> <span class="na">app</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary darken-2&#34;</span> <span class="na">dark</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h3</span><span class="p">&gt;</span>Simple Timesheet<span class="p">&lt;/</span><span class="nt">h3</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">text</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/daily&#34;</span><span class="p">&gt;</span>Daily<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">text</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-app-bar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-main</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-5&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;100%&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">&gt;&lt;/</span><span class="nt">router-view</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-main</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;App&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="c1">//
</span></span></span><span class="line"><span class="cl">    <span class="p">}),</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>All we have done here is to change the toolbar, denote where our router will render views (<code>router-view</code> - no surprise there), and wrap the entire thing in a <code>v-container</code> to provide some nice spacing on all sides.</p>
<p>Follow up with a few more changes at the project level -</p>
<ol>
<li>Delete <code>component/HelloWorld.vue</code> component - we will create our own components. Remove <code>HelloWorld</code> references from <code>App.vue</code> and <code>Home.vue</code> view.</li>
<li>Delete <code>views/Home.vue</code> and remove references to <code>Home</code> from <code>router/index.js</code>.</li>
<li>Change <code>About.vue</code> page - include whatever content you want.</li>
<li>Create a new view called <code>views/Daily.vue</code> - let them be blank for now</li>
<li>Change <code>router/index.js</code> to add references to the new view. Let us redirect &ldquo;/&rdquo; to &ldquo;/daily&rdquo; and &ldquo;/daily&rdquo; will point to the &ldquo;Daily&rdquo; view.
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ... other code
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/daily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Daily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Daily.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/weekly&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Weekly&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Weekly.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/about&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;About&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;about&#34; */</span> <span class="s2">&#34;../views/About.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ... other code
</span></span></span></code></pre></div></li>
</ol>
<p>Next, configure any additional packages.</p>
<h3 id="package-1-vuex-persist">Package 1: vuex-persist</h3>
<p><code>vuex-persist</code> enables storing of vuex values to browser local storage. This is quite handy since we don&rsquo;t want to lose values entered by the user whenever the page is refreshed, or when the website is closed.</p>
<p>Make below changes in <code>store/index.js</code> -</p>
<ol>
<li>
<p>Pull <code>vuex-persist</code> module</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VuexPersistence</span> <span class="nx">from</span> <span class="s2">&#34;vuex-persist&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">vuexLocal</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VuexPersistence</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">storage</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
<li>
<p>Include as a plugin</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">actions</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vuexLocal</span><span class="p">.</span><span class="nx">plugin</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div></li>
</ol>
<h3 id="package-2-vue-shortkey">Package 2: vue-shortkey</h3>
<p>Change <code>main.js</code>. Just after the line <code>Vue.config.productionTip = false;</code> add another line..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">require</span><span class="p">(</span><span class="s2">&#34;vue-shortkey&#34;</span><span class="p">));</span>
</span></span></code></pre></div><p>This will reference the package we added earlier <code>vue-shortkey</code>, which will enable us to tie keyboard shortcuts to specific actions (e.g. create new record can be tied to <code>Ctrl + N</code>).</p>
<h3 id="additional-configuration">Additional Configuration</h3>
<p>Add a single line to <code>public/index.html</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/microtip/microtip.css&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>This will enable us to use a small, light-weight package called <code>microtip</code> to show tooltips on any UI element. See <a href="/simple-tooltip-vue">creating simple tooltips in Vue</a> for more details.</p>
<h3 id="start-your-project">Start your project</h3>
<p>It&rsquo;s time to start up stuff and see if everything&rsquo;s working.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm run serve
</span></span></code></pre></div><p>You should see your Vue application start up, and automatically open the browser with your application at <code>http://localhost:8080</code>.</p>
<p>Note that -</p>
<ul>
<li>We have not added any functionality yet - will get to that in the next section</li>
<li>UI elements like icons, iamges and fonts were not changed</li>
<li>We did not quite need a router. We could have as well used a tab component in Vuetify and be no wiser. While that keeps things simpler, router is invaluable if you start adding other views and need navigation elements for those views</li>
<li>It&rsquo;s possible to configure vuex-persist to your heart&rsquo;s content, but we are sticking to keeping things simple</li>
</ul>
<h2 id="create-supporting-components">Create Supporting Components</h2>
<p>Let&rsquo;s go ahead and create a couple of reusable components that will be used by other components.</p>
<h3 id="date-pick-component">Date Pick Component</h3>
<p>Vuetify makes creating a field with a date pick quite easy. We can create a text box and enable a date picker using <code>v-date-picker</code>.</p>
<p><img loading="lazy" src="/misc/date-picker-input-box-vuetify.gif" type="" alt="date-picker-input-box-vuetify"  /></p>
<p>But, we don&rsquo;t want to use boilerplate code each time we need to create a date field. Let us create a <code>DatePick</code> component that can be used by other components or views for any date fields.</p>
<p>Create a new file <code>components/DatePick.vue</code>. Add following code -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- Todo: This needs cleaning up - either incl. date format or remove them --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-menu</span>
</span></span><span class="line"><span class="cl">    <span class="na">ref</span><span class="o">=</span><span class="s">&#34;menu&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;menu&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:close-on-content-click</span><span class="o">=</span><span class="s">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">transition</span><span class="o">=</span><span class="s">&#34;scale-transition&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">offset-y</span>
</span></span><span class="line"><span class="cl">    <span class="na">:nudge-right</span><span class="o">=</span><span class="s">&#34;40&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">scrollable</span>
</span></span><span class="line"><span class="cl">    <span class="na">max-width</span><span class="o">=</span><span class="s">&#34;290px&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">min-width</span><span class="o">=</span><span class="s">&#34;290px&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">        <span class="na">:value</span><span class="o">=</span><span class="s">&#34;dateDisplay&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">        <span class="na">:label</span><span class="o">=</span><span class="s">&#34;label&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">append-icon</span><span class="o">=</span><span class="s">&#34;mdi-calendar&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">readonly</span>
</span></span><span class="line"><span class="cl">        <span class="na">:dense</span><span class="o">=</span><span class="s">&#34;dense&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">:hide-details</span><span class="o">=</span><span class="s">&#34;hideDetails&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">:disabled</span><span class="o">=</span><span class="s">&#34;disabled&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">click:append</span><span class="o">=</span><span class="s">&#34;menu = true&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-date-picker</span>
</span></span><span class="line"><span class="cl">      <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dateValue&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">no-title</span>
</span></span><span class="line"><span class="cl">      <span class="na">scrollable</span>
</span></span><span class="line"><span class="cl">      <span class="na">:min</span><span class="o">=</span><span class="s">&#34;min&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:max</span><span class="o">=</span><span class="s">&#34;max&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="err">@</span><span class="na">input</span><span class="o">=</span><span class="s">&#34;menu = false&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">v-date-picker</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-menu</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">label</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">appendIcon</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;mdi-calendar&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">field</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dense</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">hideDetails</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">disabled</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">min</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">dt</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="nx">dt</span><span class="p">.</span><span class="nx">setDate</span><span class="p">(</span><span class="nx">dt</span><span class="p">.</span><span class="nx">getDate</span><span class="p">()</span> <span class="o">-</span> <span class="mi">30</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nx">dt</span><span class="p">.</span><span class="nx">toISOString</span><span class="p">().</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">max</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="k">default</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">dt</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="nx">dt</span><span class="p">.</span><span class="nx">setDate</span><span class="p">(</span><span class="nx">dt</span><span class="p">.</span><span class="nx">getDate</span><span class="p">()</span> <span class="o">+</span> <span class="mi">365</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nx">dt</span><span class="p">.</span><span class="nx">toISOString</span><span class="p">().</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">menu</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">modal</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">menu2</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">localField</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">dateValue</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dateDisplay</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">get</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">dateValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">set</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;update:field&#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">localFieldFmt</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">get</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">formatDate</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">localField</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">set</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;update:field&#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">watch</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">localFieldFmt</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;update:field&#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dateValue</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;update:field&#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">dateValue</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">field</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// this.localField = this.parseDate(this.field);
</span></span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">test</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;wow&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">formatDate</span><span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">date</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="p">[</span><span class="nx">year</span><span class="p">,</span> <span class="nx">month</span><span class="p">,</span> <span class="nx">day</span><span class="p">]</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34;-&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//   return `${day}/${month}/${year}`;
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sb">`</span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">month</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">day</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">parseDate</span><span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">date</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="p">[</span><span class="nx">day</span><span class="p">,</span> <span class="nx">month</span><span class="p">,</span> <span class="nx">year</span><span class="p">]</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34;-&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sb">`</span><span class="si">${</span><span class="nx">year</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">month</span><span class="p">.</span><span class="nx">padStart</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s2">&#34;0&#34;</span><span class="p">)</span><span class="si">}</span><span class="sb">-</span><span class="si">${</span><span class="nx">day</span><span class="p">.</span><span class="nx">padStart</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s2">&#34;0&#34;</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">setLocalFieldFmt</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">localFieldFmt</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">localField</span> <span class="o">=</span> <span class="nx">val</span> <span class="o">?</span> <span class="k">this</span><span class="p">.</span><span class="nx">parseDate</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="o">:</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Don&rsquo;t quite worry if you don&rsquo;t get what&rsquo;s going on here. We are doing a couple of things -</p>
<ol>
<li>Create a text field input box that can take in user inputs</li>
<li>Enable date control using <code>v-date-picker</code></li>
<li>Raise an event whenever the date is set so that the calling component can set a value based on user input</li>
<li>We can also parse date values to handle international dates, but we will not quite use that here</li>
</ol>
<p>We can now use this component in any other view or component like so -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">DatePick</span>
</span></span><span class="line"><span class="cl">  <span class="na">name</span><span class="o">=</span><span class="s">&#34;todayDate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">:field</span><span class="o">=</span><span class="s">&#34;dayDate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="err">@</span><span class="na">update:field</span><span class="o">=</span><span class="s">&#34;setDayDate($event)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">:dense</span><span class="o">=</span><span class="s">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;&lt;/</span><span class="nt">DatePick</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Here -</p>
<ul>
<li><code>dayDate</code> is a field (can be from Vuex or at the component level - we will see how this is used shortly)</li>
<li>On <code>update:field</code> event (which corresponds to the date input by user) we call a method called <code>setDayDate</code></li>
</ul>
<p>See <a href="/vuetify-input-field-date-picker/">reusable date picker field for Vuetify</a> for more details about the date picker field.</p>
<h3 id="confirm-component">Confirm component</h3>
<p>It is a common UI experience to ask for confirmation before allowing specific user actions - especially if they are irreversible. Vuetify has a <code>v-dialog</code> component to show a popup to that purpose, but it would need some boilerplate code to enable it each time to ask a question. Let&rsquo;s create a <code>Confirm</code> component to make it easier.</p>
<p>Create a new file <code>components/Confirm.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-dialog</span>
</span></span><span class="line"><span class="cl">    <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dialog&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:max-width</span><span class="o">=</span><span class="s">&#34;options.width&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:style</span><span class="o">=</span><span class="s">&#34;{ zIndex: options.zIndex }&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="err">@</span><span class="na">keydown</span><span class="err">.</span><span class="na">esc</span><span class="o">=</span><span class="s">&#34;cancel&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">dark</span> <span class="na">:color</span><span class="o">=</span><span class="s">&#34;options.color&#34;</span> <span class="na">dense</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-toolbar-title</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-body-2 font-weight-bold grey--text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{ title }}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-toolbar-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card-text</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-show</span><span class="o">=</span><span class="s">&#34;!!message&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;pa-4 black--text&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-html</span><span class="o">=</span><span class="s">&#34;message&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">v-card-text</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card-actions</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;!options.noconfirm&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">text</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;body-2 font-weight-bold&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">native</span><span class="o">=</span><span class="s">&#34;cancel&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>Cancel<span class="p">&lt;/</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;body-2 font-weight-bold&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">native</span><span class="o">=</span><span class="s">&#34;agree&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>OK<span class="p">&lt;/</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card-actions</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-dialog</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Confirm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">dialog</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">resolve</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">reject</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">title</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;grey lighten-3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">width</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">zIndex</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">noconfirm</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">open</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">options</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span> <span class="o">=</span> <span class="nx">resolve</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">reject</span> <span class="o">=</span> <span class="nx">reject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">agree</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">cancel</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>In the above code -</p>
<ul>
<li>we show a message popup using <code>v-dialog</code>. The message is passed to the component using props</li>
<li>we pass along the user action (<code>yes</code> or <code>no</code> for example) so that the caller can take action based on the answer</li>
</ul>
<p>We use the <code>Confirm</code> component in two parts.</p>
<ol>
<li>
<p>First, create a reference to <code>Confirm</code> in <code>&lt;template&gt;</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- other code --&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">confirm</span> <span class="na">ref</span><span class="o">=</span><span class="s">&#34;confirm&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- other code --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Import the component.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// other code
</span></span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Confirm</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../components/Confirm&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// other code
</span></span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
<li>
<p>Call the confirm dialog whenever needed</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// other code
</span></span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">deleteRecordWithConfirm</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">confirm</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Confirm Delete&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Are you sure you want to delete this record?&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">deleteRecord</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// other code
</span></span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div></li>
</ol>
<p>Again, don&rsquo;t worry too much into how confirm works - you will get time to see much more later.</p>
<p>Now that everything&rsquo;s wired up and we created baseline infrastructure, it is time to write our core functionality.</p>
<h2 id="create-daily-component">Create Daily Component</h2>
<p>We already created a view called <code>Daily.vue</code>. We will add a simple form and some code to enable users to create/edit/view timesheet entries.</p>
<p>Add the below code -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;white&#34;</span> <span class="na">width</span><span class="o">=</span><span class="s">&#34;70%&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-black h6&#34;</span><span class="p">&gt;</span>Daily Timesheet<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">small</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;resetAfterConfirm&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Reset timesheet. Delete all entries below.&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-delete-sweep<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span> Reset
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-menu</span> <span class="na">offset-y</span> <span class="na">close-on-content-click</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on, attrs }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">              <span class="na">small</span>
</span></span><span class="line"><span class="cl">              <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Download timesheet.&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">v-bind</span><span class="o">=</span><span class="s">&#34;attrs&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-download<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              Export
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-list</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-list-item</span>
</span></span><span class="line"><span class="cl">              <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(item, index) in downloadItems&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;invokeAction(item.action)&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-list-item-title</span><span class="p">&gt;</span>{{ item.title }}<span class="p">&lt;/</span><span class="nt">v-list-item-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-list-item</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">v-list</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-menu</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">small</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;newDayTime&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-shortkey</span><span class="o">=</span><span class="s">&#34;[&#39;alt&#39;, &#39;shift&#39;, &#39;n&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">shortkey</span><span class="o">=</span><span class="s">&#34;newDayTime()&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Create new time. [alt+shift+n]&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-plus<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>Add
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pl-3 pr-3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;todayDate&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold grey--text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            Date
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">DatePick</span>
</span></span><span class="line"><span class="cl">            <span class="na">name</span><span class="o">=</span><span class="s">&#34;todayDate&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">:field</span><span class="o">=</span><span class="s">&#34;dayDate&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="err">@</span><span class="na">update:field</span><span class="o">=</span><span class="s">&#34;setDayDate($event)&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">:dense</span><span class="o">=</span><span class="s">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;&lt;/</span><span class="nt">DatePick</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-h6&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            Total Hours:
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold&#34;</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              {{ totalTime }}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-card</span>
</span></span><span class="line"><span class="cl">          <span class="na">flat</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;transparent&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">height</span><span class="o">=</span><span class="s">&#34;400px&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;pl-3&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">style</span><span class="o">=</span><span class="s">&#34;overflow-y:auto; overflow-x:hidden&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-form</span> <span class="err">@</span><span class="na">submit</span><span class="err">.</span><span class="na">prevent</span><span class="o">=</span><span class="s">&#34;addEntry&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-row</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span> <span class="na">dense</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;10&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-col</span>
</span></span><span class="line"><span class="cl">                <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(dayTime, index) in dayTimes&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">v-row</span> <span class="na">dense</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">                      <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;What work is this for?&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dayTime[&#39;desc&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">dense</span>
</span></span><span class="line"><span class="cl">                      <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">                      <span class="na">autofocus</span>
</span></span><span class="line"><span class="cl">                      <span class="err">@</span><span class="na">focus</span><span class="o">=</span><span class="s">&#34;activeIndex = index&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">hide-details</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;10&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;2&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">                      <span class="na">dense</span>
</span></span><span class="line"><span class="cl">                      <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Hours&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dayTime[&#39;time&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">                      <span class="na">type</span><span class="o">=</span><span class="s">&#34;number&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">reverse</span>
</span></span><span class="line"><span class="cl">                      <span class="err">@</span><span class="na">focus</span><span class="o">=</span><span class="s">&#34;activeIndex = index&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">hide-details</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;2&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">                      <span class="na">fab</span>
</span></span><span class="line"><span class="cl">                      <span class="na">icon</span>
</span></span><span class="line"><span class="cl">                      <span class="na">small</span>
</span></span><span class="line"><span class="cl">                      <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;delDayTimeByIndex(activeIndex)&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">v-shortkey</span><span class="o">=</span><span class="s">&#34;[&#39;alt&#39;, &#39;shift&#39;, &#39;del&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="err">@</span><span class="na">shortkey</span><span class="o">=</span><span class="s">&#34;delDayTimeByIndex(activeIndex)&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Delete this entry. [alt+shift+del]&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">data-microtip-size</span><span class="o">=</span><span class="s">&#34;medium&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span><span class="p">&gt;</span>mdi-delete<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">v-form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">confirm</span> <span class="na">ref</span><span class="o">=</span><span class="s">&#34;confirm&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span><span class="p">,</span> <span class="nx">mapMutations</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Daily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">DatePick</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../components/DatePick&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Confirm</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../components/Confirm&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">activeIndex</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">downloadItems</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;CSV&#34;</span><span class="p">,</span> <span class="nx">action</span><span class="o">:</span> <span class="s2">&#34;generateDownloadCsv&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;TXT&#34;</span><span class="p">,</span> <span class="nx">action</span><span class="o">:</span> <span class="s2">&#34;generateDownloadTxt&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapState</span><span class="p">(</span><span class="s2">&#34;daystr&#34;</span><span class="p">,</span> <span class="p">[</span><span class="s2">&#34;dayTimes&#34;</span><span class="p">,</span> <span class="s2">&#34;dayDate&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">totalTime</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nx">sum</span><span class="p">,</span> <span class="nx">element</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">sum</span> <span class="o">+</span> <span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;time&#34;</span><span class="p">])</span> <span class="o">||</span> <span class="mi">0</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">resetDay</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapMutations</span><span class="p">(</span><span class="s2">&#34;daystr&#34;</span><span class="p">,</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;newDayTime&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;setActiveDayTime&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;setDayDate&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;resetDay&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;delDayTimeByIndex&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">]),</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">resetAfterConfirm</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">confirm</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Confirm Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Are you sure you want to reset timesheet? You will lose all data on this page.&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">resetDay</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">invokeAction</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">[</span><span class="nx">action</span><span class="p">]();</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">generateDownloadCsv</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="s2">&#34;data:text/csv&#34;</span><span class="p">,</span> <span class="s2">&#34;csv&#34;</span><span class="p">,</span> <span class="s2">&#34;,&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">generateDownloadTxt</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="s2">&#34;data:text/txt&#34;</span><span class="p">,</span> <span class="s2">&#34;txt&#34;</span><span class="p">,</span> <span class="s2">&#34; - &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="nx">filetype</span><span class="p">,</span> <span class="nx">extn</span><span class="p">,</span> <span class="nx">separator</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">strDayTimes</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">filetype</span><span class="si">}</span><span class="sb">;charset=utf-8,Timesheet for Date: </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">dayDate</span><span class="si">}</span><span class="sb"> \n\rTotal Time: </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">totalTime</span><span class="si">}</span><span class="sb"> hours \n\rTime Records\n===============================\n`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">strDayTimes</span> <span class="o">+=</span> <span class="sb">`Description</span><span class="si">${</span><span class="nx">separator</span><span class="si">}</span><span class="sb">Time (Hours)\n===============================\n`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">strDayTimes</span> <span class="o">+=</span> <span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">element</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="sb">`</span><span class="si">${</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;desc&#34;</span><span class="p">]</span><span class="si">}${</span><span class="nx">separator</span><span class="si">}${</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;time&#34;</span><span class="p">]</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s2">&#34;\n&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/(^\[)|(\]$)/gm</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">fileData</span> <span class="o">=</span> <span class="nb">encodeURI</span><span class="p">(</span><span class="nx">strDayTimes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="nx">fileData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">,</span> <span class="sb">`timesheet.</span><span class="si">${</span><span class="nx">extn</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">link</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>There are quite a few things going on here. Let us dissect them in detail.</p>
<h3 id="start-up">Start up</h3>
<p><code>mounted()</code> method in a Vue component helps us to &ldquo;hook&rdquo; into the component start up and run some code each time the component gets displayed.</p>
<p>We are including the following code in <code>mounted</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">resetDay</span><span class="p">();</span>
</span></span></code></pre></div><p>This will call a method <code>resetDay</code> only if <code>dayTimes</code> array does not have any elements. <code>resetDay</code> is outlined in the Vuex store. It will -</p>
<ol>
<li>Reset the date to today&rsquo;s date</li>
<li>Delete all entries in the timesheet</li>
</ol>
<h3 id="ui-elements">UI Elements</h3>
<p>Everything within <code>&lt;template&gt;</code> tags form our UI. Here we use Vuetify grid, ready-made components from Vuetify like button, text input box, and more to create our timesheet.</p>
<h4 id="toolbar">Toolbar</h4>
<p>Toolbar consists of a couple of buttons and a menu item to show export options.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-black h6&#34;</span><span class="p">&gt;</span>Daily Timesheet<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">    <span class="na">small</span>
</span></span><span class="line"><span class="cl">    <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;resetAfterConfirm&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Reset timesheet. Delete all entries below.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-delete-sweep<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span> Reset
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-menu</span> <span class="na">offset-y</span> <span class="na">close-on-content-click</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on, attrs }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="na">small</span>
</span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Download timesheet.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-bind</span><span class="o">=</span><span class="s">&#34;attrs&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-download<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        Export
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-list</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-list-item</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(item, index) in downloadItems&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;invokeAction(item.action)&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-list-item-title</span><span class="p">&gt;</span>{{ item.title }}<span class="p">&lt;/</span><span class="nt">v-list-item-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-list-item</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-list</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-menu</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">    <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">small</span>
</span></span><span class="line"><span class="cl">    <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;newDayTime&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">v-shortkey</span><span class="o">=</span><span class="s">&#34;[&#39;alt&#39;, &#39;shift&#39;, &#39;n&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="err">@</span><span class="na">shortkey</span><span class="o">=</span><span class="s">&#34;newDayTime()&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Create new time. [alt+shift+n]&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">dark</span><span class="p">&gt;</span>mdi-plus<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>Add
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>You may note that -</p>
<ul>
<li><code>v-menu</code> pulls in the list of menu items using a <code>&lt;v-list&gt;</code> from a variable called <code>downloadItems</code>. This variable is specific to this Vue component and is defined in <code>data</code> section</li>
<li>we used <code>microtip</code> required parameters like <code>aria-label</code>, <code>data-microtip-position</code> and <code>role</code> to create tooltips on button</li>
<li>we called methods defined in the <code>script</code> section using the magical <code>@click</code> event from buttons (<code>v-btn</code>)</li>
<li>buttons use <code>v-shortkey</code> to denote the shortcut key combination to simulate button click and <code>@shotcutkey</code> to denote which method should be called on click</li>
</ul>
<h4 id="header">Header</h4>
<p>Create -</p>
<ul>
<li>a date field that the user can update. Use the <code>DatePick</code> component created earlier</li>
<li>a field that shows the total time entered. Use <code>totalTime</code> computed variable - details on the computed variable in the <code>computed</code> section</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">DatePick</span>
</span></span><span class="line"><span class="cl">  <span class="na">name</span><span class="o">=</span><span class="s">&#34;todayDate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">:field</span><span class="o">=</span><span class="s">&#34;dayDate&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="err">@</span><span class="na">update:field</span><span class="o">=</span><span class="s">&#34;setDayDate($event)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">:dense</span><span class="o">=</span><span class="s">&#34;true&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;&lt;/</span><span class="nt">DatePick</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-h6&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  Total Hours:
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold&#34;</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span> {{ totalTime }} <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><h4 id="timesheet-entries">Timesheet entries</h4>
<p>Show the list of timesheet entries recorded in <code>dayTimes</code> array with the below code block -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(dayTime, index) in dayTimes&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-row</span> <span class="na">dense</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">        <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;What work is this for?&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dayTime[&#39;desc&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">dense</span>
</span></span><span class="line"><span class="cl">        <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">        <span class="na">autofocus</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">focus</span><span class="o">=</span><span class="s">&#34;activeIndex = index&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">hide-details</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;10&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;2&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">        <span class="na">dense</span>
</span></span><span class="line"><span class="cl">        <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Hours&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dayTime[&#39;time&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">        <span class="na">type</span><span class="o">=</span><span class="s">&#34;number&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">reverse</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">focus</span><span class="o">=</span><span class="s">&#34;activeIndex = index&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">hide-details</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;2&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="na">fab</span>
</span></span><span class="line"><span class="cl">        <span class="na">icon</span>
</span></span><span class="line"><span class="cl">        <span class="na">small</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;delDayTimeByIndex(activeIndex)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-shortkey</span><span class="o">=</span><span class="s">&#34;[&#39;alt&#39;, &#39;shift&#39;, &#39;del&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="err">@</span><span class="na">shortkey</span><span class="o">=</span><span class="s">&#34;delDayTimeByIndex(activeIndex)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;Delete this entry. [alt+shift+del]&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">data-microtip-size</span><span class="o">=</span><span class="s">&#34;medium&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span><span class="p">&gt;</span>mdi-delete<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="computed-variables">Computed variables</h3>
<p>Computed variables allow us to use calculated values, values that dymamically change at different stages of the component based on component state, data variables, external data, user actions and so on.</p>
<p>We created two different types of variables here -</p>
<ol>
<li>Pull in the Vuex state variables from <code>daystr</code> module with <code>...mapState(&quot;daystr&quot;, [&quot;dayTimes&quot;, &quot;dayDate&quot;])</code></li>
<li>Create a <code>totalTime</code> value that will add up all time entries and provide the total time entered on the timesheet. There are multiple ways of doing this including a simple <code>for</code> statement to loop through the <code>dayTimes</code> array, but we have used some shortcut methods to keep the code clean 😜</li>
</ol>
<p>The overall <code>computed</code> code block is below -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">...</span><span class="nx">mapState</span><span class="p">(</span><span class="s2">&#34;daystr&#34;</span><span class="p">,</span> <span class="p">[</span><span class="s2">&#34;dayTimes&#34;</span><span class="p">,</span> <span class="s2">&#34;dayDate&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">totalTime</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nx">sum</span><span class="p">,</span> <span class="nx">element</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">sum</span> <span class="o">+</span> <span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;time&#34;</span><span class="p">])</span> <span class="o">||</span> <span class="mi">0</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><h3 id="methods">Methods</h3>
<p>Most of the methods are mutations defined in the Vuex module <code>dailystr</code>. These are referenced in our component with the following line -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="nx">mapMutations</span><span class="p">(</span><span class="s2">&#34;daystr&#34;</span><span class="p">,</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;newDayTime&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;setActiveDayTime&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;setDayDate&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;resetDay&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;delDayTimeByIndex&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">]),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Beyond Vuex, we have a few more methods -</p>
<h4 id="method-1-invokeaction">Method 1: InvokeAction</h4>
<p>This method simply calls another method. It is used in a list so that we can call methods dynamically while we are defining the list.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">invokeAction</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">[</span><span class="nx">action</span><span class="p">]();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><h4 id="method-2-confirm-and-reset-timesheet">Method 2: Confirm and reset timesheet</h4>
<p>This method gets invoked when user clicks on &ldquo;Reset&rdquo; button. Ask user to confirm reset and call a Vuex mutation that will reset the <code>dayTimes</code> array and reset date to today&rsquo;s date.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">async</span> <span class="nx">resetAfterConfirm</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">confirm</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Confirm Reset&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;Are you sure you want to reset timesheet? You will lose all data on this page.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">resetDay</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><h4 id="methods-3--4-generatedownload-methods">Methods 3 &amp; 4: generateDownLoad methods</h4>
<p>We invoke two distinct methods when user clicks on export button - one of them facilitates export to CSV file format, while the other option enables a text export.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">generateDownloadCsv</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">this</span><span class="p">.</span><span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="s2">&#34;data:text/csv&#34;</span><span class="p">,</span> <span class="s2">&#34;csv&#34;</span><span class="p">,</span> <span class="s2">&#34;,&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="nx">generateDownloadTxt</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">this</span><span class="p">.</span><span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="s2">&#34;data:text/txt&#34;</span><span class="p">,</span> <span class="s2">&#34;txt&#34;</span><span class="p">,</span> <span class="s2">&#34; - &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>Both these methods call <code>generateExportAndDownload</code>, which will export the timesheet entries to a file based on input arguments. We could have called <code>generateExportAndDownload</code> method directly from the <code>template</code> HTML with arguments, but we used different methods for any future scalability. (yes, this is sarcasm)</p>
<h4 id="method-5-generateexportanddownload-method">Method 5: generateExportAndDownload method</h4>
<p><code>generateExportAndDownload</code> will -</p>
<ul>
<li>take three arguments for the export file type, the extension to be used for the export file and the separator to be used between the timesheet description and number of hours</li>
<li>fetch the entries from the Vuex store variables (the &ldquo;state&rdquo; values) <code>dayDate</code> and <code>dayTimes</code></li>
<li>create a string for export by joining some informational statements and the <code>dayTimes</code> array</li>
<li>create an <code>a</code> tag with the content as newly created string</li>
<li>simulate a click on the <code>a</code> link to download content to a file</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">generateExportAndDownload</span><span class="p">(</span><span class="nx">filetype</span><span class="p">,</span> <span class="nx">extn</span><span class="p">,</span> <span class="nx">separator</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">strDayTimes</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">filetype</span><span class="si">}</span><span class="sb">;charset=utf-8,Timesheet for Date: </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">dayDate</span><span class="si">}</span><span class="sb"> \n\rTotal Time: </span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">totalTime</span><span class="si">}</span><span class="sb"> hours \n\rTime Records\n===============================\n`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">strDayTimes</span> <span class="o">+=</span> <span class="sb">`Description</span><span class="si">${</span><span class="nx">separator</span><span class="si">}</span><span class="sb">Time (Hours)\n===============================\n`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">strDayTimes</span> <span class="o">+=</span> <span class="k">this</span><span class="p">.</span><span class="nx">dayTimes</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">element</span> <span class="p">=&gt;</span> <span class="sb">`</span><span class="si">${</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;desc&#34;</span><span class="p">]</span><span class="si">}${</span><span class="nx">separator</span><span class="si">}${</span><span class="nx">element</span><span class="p">[</span><span class="s2">&#34;time&#34;</span><span class="p">]</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s2">&#34;\n&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/(^\[)|(\]$)/gm</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">fileData</span> <span class="o">=</span> <span class="nb">encodeURI</span><span class="p">(</span><span class="nx">strDayTimes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;a&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;href&#34;</span><span class="p">,</span> <span class="nx">fileData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">&#34;download&#34;</span><span class="p">,</span> <span class="sb">`timesheet.</span><span class="si">${</span><span class="nx">extn</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">link</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><h2 id="modify-vuex-store">Modify Vuex store</h2>
<p>The <code>Daily</code> timesheet component references Vuex store and specifically a module called <code>dailystr</code>. Let us include that.</p>
<p>Create a new file called <code>store/dailystr.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">namespaced</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;daystr&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dayDate</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">dayTimes</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">activeDayTime</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">newDayTime</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> <span class="nx">desc</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nx">time</span><span class="o">:</span> <span class="mi">1</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">setActiveDayTime</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">activeDayTime</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">delDayTimeByIndex</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">dayTimes</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">index</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">setDayDate</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">dayDate</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">resetDay</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">dayDate</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">().</span><span class="nx">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">dayTimes</span> <span class="o">=</span> <span class="p">[{</span> <span class="nx">desc</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nx">time</span><span class="o">:</span> <span class="mi">1</span> <span class="p">}];</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>We create a bunch of variables to store time entries in a day, and also created <code>mutations</code> that allow us to change those variable values.</p>
<p>Change <code>store/index.js</code> to include the newly created module. The complete code is outlined below.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vuex</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VuexPersistence</span> <span class="nx">from</span> <span class="s2">&#34;vuex-persist&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">daystr</span> <span class="nx">from</span> <span class="s2">&#34;./daystr&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">vuexLocal</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VuexPersistence</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">storage</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">actions</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">{</span> <span class="nx">daystr</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">vuexLocal</span><span class="p">.</span><span class="nx">plugin</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>We had made the <code>vuex-persist</code> related changes at the beginning of this post.</p>
<h2 id="enable-pwa">Enable PWA</h2>
<p>If you had selected the <code>PWA</code> option while creating the project using Vue CLI, your app is automatically available as a progressive web app. You cannot test this unless you are connected using <code>https</code>. Since we don&rsquo;t really do any changes here, you can see the PWA in action after deploying the application.</p>
<h2 id="deploy">Deploy</h2>
<p>Deploying a client-application is really easy - thanks to build/deploy/host application services available today.</p>
<p>As an example, we will use <a href="https://vercel.com">vercel.com</a>.</p>
<p>Go to vercel.com, create an account if you don&rsquo;t have one, or login to your existing account.</p>
<p>On dashboard, click on <code>Import Project</code> button. Vercel will ask you for the Git repository to import and may also ask you for additional permissions to access repository.</p>
<p><img loading="lazy" src="/2020/deploy-vue-app-vercel.jpg" type="" alt="deploy-vue-app-vercel"  /></p>
<p>Once the repo is imported, Vercel should automatically identify the project as a Vue app and set build configuration. Review the configuration and click on <code>Deploy</code> to deploy your app and make it available to the world. Vercel will provide the URL that you can use to access your app.</p>
<p>The URL for your app will be available over https (e.g. <a href="https://simple-timesheet.vercel.app">simple-timesheet.vercel.app</a>). You will be able to install this app to your desktop or mobile using the functionality enabled by PWA.</p>
<h2 id="finis">Finis</h2>
<p>That&rsquo;s it - you have created and deployed a totally real-world timesheet application usign Vue, Vuetify and friends.</p>
<p>The repository with full code is at <a href="https://github.com/prashanth1k/simple-timesheet">https://github.com/prashanth1k/simple-timesheet</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create Simple Tooltips in Vue</title>
      <link>https://techformist.com/simple-tooltip-vue/</link>
      <pubDate>Wed, 21 Oct 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/simple-tooltip-vue/</guid>
      <description>&lt;p&gt;Tooltips are omnipresent, or rather have to be. While there have been excellent standardisation of user experience since we saw what Bootstrap was capable of, there are days and applications that can throw off users with strange icons, buttons and navigation. Ergo, tooltips.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2020/vuetify-tool-tip.gif&#34; type=&#34;&#34; alt=&#34;vuetify-tool-tip&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;The humble tooltip can provide helpful hints, short messages and guide user on what a particular button, text box or any other element will do before clicking the thingy and causing destruction of a planet.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Tooltips are omnipresent, or rather have to be. While there have been excellent standardisation of user experience since we saw what Bootstrap was capable of, there are days and applications that can throw off users with strange icons, buttons and navigation. Ergo, tooltips.</p>
<p><img loading="lazy" src="/2020/vuetify-tool-tip.gif" type="" alt="vuetify-tool-tip"  /></p>
<p>The humble tooltip can provide helpful hints, short messages and guide user on what a particular button, text box or any other element will do before clicking the thingy and causing destruction of a planet.</p>
<p>Vue has more than few options for creating tooltips.</p>
<h2 id="use-tooltip-from-ui-library">Use tooltip from UI library</h2>
<p>Every UI library has support for tooltips in some form. Take Vuetify as an example.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-center d-flex align-center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-tooltip</span> <span class="na">bottom</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on, attrs }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span> <span class="na">dark</span> <span class="na">v-bind</span><span class="o">=</span><span class="s">&#34;attrs&#34;</span> <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span><span class="p">&gt;</span>Button<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>Yes, this is a button<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-tooltip</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Just encapsulate any component with <code>v-tooltip</code> and you are all set.</p>
<blockquote>
<p>See how you can <a href="/tooltip-vuetify">create reusable tooltip in Vuetify</a>.</p>
</blockquote>
<p>Other libraries are somewhat better - BootstrapVue, Buefy etc. have simple ways to enable tooltips.</p>
<p>I typically prefer to use the tooltip provided by the styling libraries -</p>
<ul>
<li>has uniform user experience as compared to rest of the application</li>
<li>(mostly) has no/minimal overhead since we are downloading other components anyway</li>
</ul>
<p>But as you can see from the Vuetify example, it is no where near to ideal. Also, you may not use styling libraries for whatever strange reason and we don&rsquo;t want you to be stuck without tooltips. See those options next.</p>
<h2 id="tooltip-using-js">Tooltip using JS</h2>
<p>We are accustomed to someone else doing the hard work in the Javascript world. For Vue that happens to be <a href="https://github.com/Akryum">@akryum</a> from the Vue core team.</p>
<p>To get started, install <code>v-tooltip</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm install --save v-tooltip
</span></span></code></pre></div><p>Refer the package in the <code>main.js</code> file in your Vue project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VTooltip</span> <span class="nx">from</span> <span class="s2">&#34;v-tooltip&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VTooltip</span><span class="p">);</span>
</span></span></code></pre></div><p>.. and start using the tooltip.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">  <span class="na">v-tooltip</span><span class="o">=</span><span class="s">&#34;{
</span></span></span><span class="line"><span class="cl"><span class="s">  content: msg,
</span></span></span><span class="line"><span class="cl"><span class="s">  placement: placement,
</span></span></span><span class="line"><span class="cl"><span class="s">}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  Hover me
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span></code></pre></div><p><img loading="lazy" src="/2020/v-tooltip-vue.gif" type="" alt="v-tooltip-vue"  /></p>
<p>See <a href="https://akryum.github.io/v-tooltip/#/">v-tooltip demo and code</a>.</p>
<p>This way of enabling tooltip in Vue projects is really simple. But, the javascript code (<code>v-tooltip</code> uses <code>popper.js</code>), (minimal) CSS and any other components add up to the long list of components to be downloaded by the browser.</p>
<p>Bundlephobia reports <code>v-tooltip</code> as being 64.7kB minified and 19.9kB gzipped. Not ideal at all. Other options like <code>tooltip.js</code>, <code>tippy.js</code> etc. do exist, but none of they clock ~8-13 kB upwards minified+gzipped.</p>
<h2 id="simple-css-for-tooltips">Simple CSS for Tooltips</h2>
<p>Tooltips can be created for any component using pure CSS. While this option may not be super powerful but more than makes up for that with it&rsquo;s lightweight nature.</p>
<p>There are multiple options to enable tooltip using CSS - balloon-css, hint.css and microtip are popular options. All these clock ~1KB minified+gzipped.</p>
<p>For example, this is what we need to do to use microtip.</p>
<p>Add the CSS reference in <code>index.html</code> in your Vue project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/microtip/microtip.css&#34;</span> <span class="p">/&gt;</span>
</span></span></code></pre></div><p>Start using the tooltip.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">  <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;This is a cool tooltip&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">data-microtip-position</span><span class="o">=</span><span class="s">&#34;bottom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">role</span><span class="o">=</span><span class="s">&#34;tooltip&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>You could also install <code>microtip</code> as a package and reference that in the project.</p>
<p>Tooltip using CSS is simple and effective!</p>
<h2 id="finis">Finis</h2>
<p>Using tooltips based on CSS is super useful, and is my preferred option (whenever UI libraries fall flat that is). But, I totally understand popular libraries like <code>v-tooltip</code> have their place and am cool with that too :)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Reusable Confirmation Dialog in Vuetify</title>
      <link>https://techformist.com/reusable-confirmation-dialog-vuetify/</link>
      <pubDate>Wed, 30 Sep 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/reusable-confirmation-dialog-vuetify/</guid>
      <description>&lt;p&gt;Dialogs are those thingies that popup on your UI and are handy to allow users to perform actions without navigating away from the screen.&lt;/p&gt;
&lt;p&gt;For example, here&amp;rsquo;s one that &lt;a href=&#34;https://techformist.com/dialog-overlay-vuetify/&#34;&gt;enables users to create new request&lt;/a&gt;..&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/misc/vuetify-dialog-popup-component.gif&#34; type=&#34;&#34; alt=&#34;vuetify-dialog-new-request&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;.. and here&amp;rsquo;s an &lt;a href=&#34;https://vuetifyjs.com/en/components/dialogs/#usage&#34;&gt;example from Vuetify site&lt;/a&gt; that shows a short message.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2020/vuetify-popup-dialog.gif&#34; type=&#34;&#34; alt=&#34;vuetify-popup-dialog&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;v-dialog&lt;/code&gt; component is the recommended way to show confirmation dialogs too.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2020/vuetify-confirm-dialog.gif&#34; type=&#34;&#34; alt=&#34;vuetify-confirm-dialog&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;While confirmation dialogs are great, they could get annoying if used in three hundred different places. For e.g. what do you do if you need a yes/no confirmation dialog?&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Dialogs are those thingies that popup on your UI and are handy to allow users to perform actions without navigating away from the screen.</p>
<p>For example, here&rsquo;s one that <a href="/dialog-overlay-vuetify/">enables users to create new request</a>..</p>
<p><img loading="lazy" src="/misc/vuetify-dialog-popup-component.gif" type="" alt="vuetify-dialog-new-request"  /></p>
<p>.. and here&rsquo;s an <a href="https://vuetifyjs.com/en/components/dialogs/#usage">example from Vuetify site</a> that shows a short message.</p>
<p><img loading="lazy" src="/2020/vuetify-popup-dialog.gif" type="" alt="vuetify-popup-dialog"  /></p>
<p>The <code>v-dialog</code> component is the recommended way to show confirmation dialogs too.</p>
<p><img loading="lazy" src="/2020/vuetify-confirm-dialog.gif" type="" alt="vuetify-confirm-dialog"  /></p>
<p>While confirmation dialogs are great, they could get annoying if used in three hundred different places. For e.g. what do you do if you need a yes/no confirmation dialog?</p>
<p>Fortunately, we can create a distinct component for reusable confirmation dialogs as easily. Here&rsquo;s a simple way to do that.</p>
<p>First, create a component <code>ConfirmDlg.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-dialog</span>
</span></span><span class="line"><span class="cl">    <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dialog&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:max-width</span><span class="o">=</span><span class="s">&#34;options.width&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:style</span><span class="o">=</span><span class="s">&#34;{ zIndex: options.zIndex }&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="err">@</span><span class="na">keydown</span><span class="err">.</span><span class="na">esc</span><span class="o">=</span><span class="s">&#34;cancel&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">dark</span> <span class="na">:color</span><span class="o">=</span><span class="s">&#34;options.color&#34;</span> <span class="na">dense</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-toolbar-title</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-body-2 font-weight-bold grey--text&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{ title }}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-toolbar-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card-text</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-show</span><span class="o">=</span><span class="s">&#34;!!message&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;pa-4 black--text&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-html</span><span class="o">=</span><span class="s">&#34;message&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">v-card-text</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card-actions</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;!options.noconfirm&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">text</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;body-2 font-weight-bold&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">native</span><span class="o">=</span><span class="s">&#34;cancel&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>Cancel<span class="p">&lt;/</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;body-2 font-weight-bold&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">outlined</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">native</span><span class="o">=</span><span class="s">&#34;agree&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>OK<span class="p">&lt;/</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card-actions</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-dialog</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;ConfirmDlg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">dialog</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">resolve</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">reject</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">message</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">title</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;grey lighten-3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">width</span><span class="o">:</span> <span class="mi">400</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">zIndex</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">noconfirm</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">open</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">title</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="nx">message</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">options</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span> <span class="o">=</span> <span class="nx">resolve</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">reject</span> <span class="o">=</span> <span class="nx">reject</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">agree</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">cancel</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">dialog</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Next, use the confirm dialog in a component.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- other code --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">ConfirmDlg</span> <span class="na">ref</span><span class="o">=</span><span class="s">&#34;confirm&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">ConfirmDlg</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;./ConfirmDlg&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">delRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">confirm</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Confirm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Are you sure you want to delete this record?&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">deleteRecord</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">deleteRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Record deleted.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And voila.</p>
<p><img loading="lazy" src="/2020/vuetify-confirm-delete-dialog.gif" type="" alt="vuetify-confirm-delete-dialog"  /></p>
<p>Here&rsquo;s a <a href="https://codepen.io/techformist/pen/ExyVWdX">Codepen with a working example</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Request Redirect for Nginx in SPAs</title>
      <link>https://techformist.com/request-redirect-nginx-spa/</link>
      <pubDate>Wed, 09 Sep 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/request-redirect-nginx-spa/</guid>
      <description>&lt;p&gt;Redirection can be easy enough on Nginx. Let&amp;rsquo;s see how we can utilise that for our single page applications coded in Vue, React, etc.&lt;/p&gt;
&lt;h2 id=&#34;nginx-and-redirection&#34;&gt;Nginx and Redirection&lt;/h2&gt;
&lt;p&gt;Nginx configuration is simple but powerful. All we need is a couple of lines to setup our server.&lt;/p&gt;
&lt;p&gt;Add the below lines to the Nginx configuration files (e.g. in &lt;code&gt;/etc/nginx/sites-enabled/domain.com.conf&lt;/code&gt; in Ubuntu).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;server {
    listen        80;
    server_name   domain.com;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Start the Nginx server, and lo and behold - our web server is ready to serve magic at port 80. Go to &lt;code&gt;domain.com&lt;/code&gt; in browser and see your beautiful app.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Redirection can be easy enough on Nginx. Let&rsquo;s see how we can utilise that for our single page applications coded in Vue, React, etc.</p>
<h2 id="nginx-and-redirection">Nginx and Redirection</h2>
<p>Nginx configuration is simple but powerful. All we need is a couple of lines to setup our server.</p>
<p>Add the below lines to the Nginx configuration files (e.g. in <code>/etc/nginx/sites-enabled/domain.com.conf</code> in Ubuntu).</p>
<pre tabindex="0"><code>server {
    listen        80;
    server_name   domain.com;
}
</code></pre><p>Start the Nginx server, and lo and behold - our web server is ready to serve magic at port 80. Go to <code>domain.com</code> in browser and see your beautiful app.</p>
<p>We can add redirection in the mix with a single line.</p>
<pre tabindex="0"><code>server {
    listen        80;
    server_name   domain.com;
    return        301 http://other_domain.com$request_uri;
}
</code></pre><p>This is a simple <code>301</code> redirection that informs requester (i.e., browser) that the web page at <code>domain.com</code> is redirected to <code>other_domain.com</code> permanently. Any suffix included with the original domain (e.g. <code>https://domain.com/awesome-page/1</code>) is sent to the redirected domain by using <code>$request_uri</code>. You can redirect any page to any other page within or outside the domain. We have previously seen this applied to <a href="/create-404-pages-vue-app">manage 404 error in Vue in Nginx</a>.</p>
<p>Replace <code>301</code> in the <code>return</code> statement to <code>302</code>, and you have a temporary redirect. I don&rsquo;t quite find a use case for temporary redirections, and can&rsquo;t claim to know how Google or other search engines respond to them, but.. they exist.</p>
<p>Alternatively, you could use a <code>rewrite</code> -</p>
<pre tabindex="0"><code>server {
    listen      80;

    location / {
      rewrite     ^(.*)   https://other_domain.com permanent;
    }
}
</code></pre><p>This statement indicates that all requests to the domain that server is listening to have to be redirected to some other domain.</p>
<p><code>rewrite</code> allows us to use regex and handle complex scenarios for redirection within or outside the domain.</p>
<p>For example -</p>
<pre tabindex="0"><code>server {
    # ...
    rewrite ^(/download/.*).?.*$ files.domain.com/downloads/$2;

    #...
}
</code></pre><p>I don&rsquo;t quite use Nginx to its full capacity and have rather naive considerations on when to use <code>rewrite</code> vs <code>redirect</code>-</p>
<ul>
<li>Simple use cases can be addressed by <code>redirect</code></li>
<li><code>rewrite</code> handles only 301 and 302 redirects (more on this in a bit)</li>
<li>Redirects based on conditions (e.g. */download/* should be directed to <code>files.domain.com</code> URL) are possible only using <code>rewrite</code></li>
</ul>
<p>While complex conditions are candidates for <code>rewrite</code>, I typically manage &ldquo;all things&rdquo; in the SPA or using server routes. But, there&rsquo;s more.</p>
<h2 id="nginx-redirection-in-spas">Nginx Redirection in SPAs</h2>
<p>Single page applications handle all routing on the client (well, typically). You can have something like a <code>vue-router</code> installed, throw in the routes in a <code>route.js</code> file and the application happily does all the redirections without help from server.</p>
<p>All we need from server is -</p>
<pre tabindex="0"><code>server {
    server_name domain.com;
    listen 80;
    root /usr/www/domain;
    index index.html index.htm;

    location / {
            try_files $uri $uri/ /index.html;
    }
    location /api/ {
            proxy_pass http://localhost:5000;
            proxy_set_header Host $host;
    }
}
</code></pre><p>In Ubuntu -</p>
<ul>
<li>you can enter this configuration in <code>/etc/nginx/sites-available/domain.com.conf</code> and</li>
<li>setup a link in <code>/etc/nginx/sites-enabled/domain.com.conf</code></li>
</ul>
<p>We are achieving quite a bit with these few lines -</p>
<ul>
<li>use yet another redirection directive called <code>try_files</code> to redirect any request (exceptions - see below) to <code>$uri</code> to a corresponding file with the same name accessible on web server. Point the request to <code>/index.html</code> if there is no file at <code>$uri</code>. The request is then handled by the SPA router</li>
<li>redirect any requests with <code>domain.com/api/</code> to another port on the server. This sets up reverse proxy and opens up the backend server (at port 5000) to the world through our domain</li>
</ul>
<p>See <a href="https://www.nginx.com/blog/creating-nginx-rewrite-rules/">this rewrite rules post</a> for a more detailed comparison of what each of those directives can do.</p>
<p>The above configuration sets up a full-fledged web server for your SPA, but there&rsquo;s one thing missing. That is for <code>http</code> to <code>https</code> redirection.. which is as easily done.</p>
<pre tabindex="0"><code>server {
    server_name domain.com;
    listen 80;
    return 301 https://$host$request_uri;
}

server {
    server_name domain.com;
    listen 443 ssl;
    root /usr/www/domain;
    index index.html index.htm;

    location / {
            try_files $uri $uri/ /index.html;
    }
    location /api/ {
            proxy_pass http://localhost:5000;
            proxy_set_header Host $host;
    }
}
</code></pre><p>This is all you need to setup a simple web server serving your SPA with Nginx.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Social Share Buttons in Vuetify</title>
      <link>https://techformist.com/social-share-vuetify/</link>
      <pubDate>Wed, 02 Sep 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/social-share-vuetify/</guid>
      <description>&lt;p&gt;Enabling social share buttons in Vuetify is quite easy. All you have to do is stitch together three components provided out of the box -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Speed dial&lt;/li&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Icons&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2020/social-share-buttons-vuetify.gif&#34; type=&#34;&#34; alt=&#34;social-share-buttons-vuetify&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;We will create a quick demo on &lt;a href=&#34;&#34;&gt;Codepen&lt;/a&gt;. Create a new Codepen, click on &lt;code&gt;Settings&lt;/code&gt;, navigate to &lt;code&gt;JS&lt;/code&gt; and add the below libraries -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js&#34;&gt;https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js&#34;&gt;https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click on &lt;code&gt;Save &amp;amp; Close&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;JS&lt;/code&gt; editor, paste following code to initialise Vue.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Vue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;#app&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;vuetify&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Vuetify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;methods&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;dialShare&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nx&#34;&gt;pageUrl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://techformist.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &amp;ldquo;normal&amp;rdquo; app, you would want to extract the page URL with something like &lt;code&gt;this.$route.fullURL&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Enabling social share buttons in Vuetify is quite easy. All you have to do is stitch together three components provided out of the box -</p>
<ol>
<li>Speed dial</li>
<li>Buttons</li>
<li>Icons</li>
</ol>
<p><img loading="lazy" src="/2020/social-share-buttons-vuetify.gif" type="" alt="social-share-buttons-vuetify"  /></p>
<p>We will create a quick demo on <a href="">Codepen</a>. Create a new Codepen, click on <code>Settings</code>, navigate to <code>JS</code> and add the below libraries -</p>
<ul>
<li><a href="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js">https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js</a></li>
<li><a href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js">https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js</a></li>
</ul>
<p>Click on <code>Save &amp; Close</code>.</p>
<p>In the <code>JS</code> editor, paste following code to initialise Vue.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">vuetify</span><span class="o">:</span> <span class="k">new</span> <span class="nx">Vuetify</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dialShare</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageUrl</span><span class="o">:</span> <span class="s2">&#34;https://techformist.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>In the &ldquo;normal&rdquo; app, you would want to extract the page URL with something like <code>this.$route.fullURL</code>.</p>
<p>Next, add HTML. Let&rsquo;s begin by adding two cards - one for header and the other encapsulating the body of the page.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-12 pb-12 indigo--text&#34;</span><span class="p">&gt;</span>Awesomeness<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-12 pb-12 font-weight-black&#34;</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      Build something great!
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Use Vuetify <a href="https://vuetifyjs.com/en/components/floating-action-buttons/">speed-dial component</a> to create a floating, expandable button that shows the actual sharing buttons.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span> <span class="na">align</span><span class="o">=</span><span class="s">&#34;center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-12 pb-12 indigo--text&#34;</span><span class="p">&gt;</span>Awesomeness<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-speed-dial</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;dialShare&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">absolute</span>
</span></span><span class="line"><span class="cl">        <span class="na">right</span>
</span></span><span class="line"><span class="cl">        <span class="na">bottom</span>
</span></span><span class="line"><span class="cl">        <span class="na">direction</span><span class="o">=</span><span class="s">&#34;left&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">open-on-hover</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">fab</span> <span class="na">bottom</span> <span class="na">small</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;dialShare&#34;</span><span class="p">&gt;</span>mdi-close<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">v-else</span><span class="p">&gt;</span>mdi-share-variant<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span>
</span></span><span class="line"><span class="cl">          <span class="na">dark</span>
</span></span><span class="line"><span class="cl">          <span class="na">fab</span>
</span></span><span class="line"><span class="cl">          <span class="na">bottom</span>
</span></span><span class="line"><span class="cl">          <span class="na">color</span><span class="o">=</span><span class="s">&#34;green&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">small</span>
</span></span><span class="line"><span class="cl">          <span class="na">:href</span><span class="o">=</span><span class="s">&#34;`https://wa.me/?text=Checkout%20this%20page.%20${pageUrl}`&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-icon</span><span class="p">&gt;</span>mdi-whatsapp<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-speed-dial</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c">&lt;!-- other code --&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The above code should get WhatsApp working - all we do is go to a specific URL when the button is clicked. No magic.</p>
<p>Complete the project by adding other social media so that none of your users get offended. Again, we direct user to the specific social media sites to share the page - no APIs, no apps, no nothing. This works equally beautifully in your web and mobile apps.</p>
<p><a href="https://codepen.io/techformist/pen/gOroOxK">See this Codepen</a> for the entire four lines of code.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Using Vue with Firestore</title>
      <link>https://techformist.com/using-firestore-vue/</link>
      <pubDate>Wed, 26 Aug 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/using-firestore-vue/</guid>
      <description>&lt;p&gt;Firestore can make up for a great backend application. You can get started quite easily, scale it up nicely, and in general, worry less about the &amp;ldquo;server&amp;rdquo; side of things. While it is quite easy to use Firestore as-is, using it in Vue opens up a whole new universe.&lt;/p&gt;
&lt;p&gt;But, first - let&amp;rsquo;s go through the grind of knowing why we would want to do this? And, of course, to please &lt;em&gt;everyone&lt;/em&gt; who just happens to be here reading the next exciting story on a lazy afternoon.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Firestore can make up for a great backend application. You can get started quite easily, scale it up nicely, and in general, worry less about the &ldquo;server&rdquo; side of things. While it is quite easy to use Firestore as-is, using it in Vue opens up a whole new universe.</p>
<p>But, first - let&rsquo;s go through the grind of knowing why we would want to do this? And, of course, to please <em>everyone</em> who just happens to be here reading the next exciting story on a lazy afternoon.</p>
<h2 id="firestore">Firestore?</h2>
<p><a href="https://firebase.google.com/products/firestore">Firestore</a> is a no SQL database by Google that is available on cloud. You can do exciting things like storing data and accessing data - all without maintaining a database of your own. If you are indeed truly delighted by this phenomenon -</p>
<ol>
<li>You have arrived to the great 2010&rsquo;s when everything&rsquo;s a web service - welcome! And oh, we crossed over to the new decade this year</li>
<li>What exactly are you doing here? I mean here on this Blog, on earth, and at this moment? It is time to contemplate life I guess</li>
</ol>
<p>That will be the last of the poor jokes, I promise. Onwards then.</p>
<p>Firestore is a God-send for -</p>
<ol>
<li>Small experiments and projects - you can get started for free, and have exactly zero ongoing server costs if you stay within limits</li>
<li>Large projects that require rapid scalability</li>
<li>Applications where you have enough problems and don&rsquo;t want to add a server ecosystem to the mix (e.g. mobile apps)</li>
</ol>
<p>Get started by visiting <a href="https://firebase.google.com/products/firestore">this link</a>, signing up and head over to the <a href="https://console.firebase.google.com/u/0/">console</a>.</p>
<p>Let&rsquo;s see some more of Firestore and Vue by creating a &ldquo;totally not twitter&rdquo; application. Users should be able to -</p>
<ul>
<li>Tweet</li>
<li>Retweet</li>
<li>That&rsquo;s pretty much it - this is not &ldquo;twitter&rdquo; after all</li>
</ul>
<p>This is how it looks -</p>
<p><img loading="lazy" src="/2020/fwtr-app-demo.gif" type="" alt="fwtr-app-demo"  /></p>
<p>If you are not disappointed yet (did you really think we will build Twitter in one blog post?), read on - things are going to be set on 🔥.</p>
<h2 id="getting-started">Getting Started</h2>
<p>Create a Vue project.</p>
<p>Install the Vue CLI if you have not done it before.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i -g @vue/cli
</span></span></code></pre></div><p>Create the project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">vue create fwtr
</span></span></code></pre></div><p>Answer a bunch of questions - we choose no to everything except Vue Router &amp; Vuex store. <code>vue-cli</code> installs and configures everything for you.</p>
<p>Next, install <code>vue-fire</code>, an awesome package that provides a Vue wrapper for firebase. We could use Firebase/Firestore as-is, but Vuefire just makes things a bit easy.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vuefire firebase
</span></span></code></pre></div><p>While at it, let&rsquo;s install a small toast library to show alerts and messages.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vue-toast-notification
</span></span></code></pre></div><p>Start your Vue project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm run serve
</span></span></code></pre></div><p>Head over to the <a href="https://console.firebase.google.com/u/0/">Firestore console</a>. Create a new project by clicking the big, bold button, and follow the guided wizard.</p>
<ol>
<li>Name the project</li>
<li>Enable Google Analytics and select an Analytics account (optional)</li>
</ol>
<p>You will see the below project page once you are done with the setup.</p>
<p><img loading="lazy" src="/2020/firestore-new-project.jpg" type="" alt="firestore-new-project"  /></p>
<p>Click on <code>Web</code> icon to register your web app. We don&rsquo;t need Firestore hosting, but feel free to go wild. Provide any name and click <code>Register</code> to see the API details. Copy over the details.</p>
<p>Go to <code>Cloud Firestore</code> link in your Firebase console and click on &ldquo;Create Database&rdquo;. Choose &ldquo;Start in test mode&rdquo;, select a region near you and hit &ldquo;Enable&rdquo; to create a new database.</p>
<p>In the database page, click on &ldquo;Start collection&rdquo; &gt; enter &ldquo;tweets&rdquo; as the collection name, and save. You can optionally enter one or more records in the collection.</p>
<p><img loading="lazy" src="/2020/firestore-create-collection.jpg" type="" alt="firestore-create-collection"  /></p>
<p>You and your project are all fired up now.</p>
<h2 id="firestore-configuration">Firestore configuration</h2>
<p>In your project <code>main.js</code>, add two lines -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">firestorePlugin</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuefire&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">firestorePlugin</span><span class="p">);</span>
</span></span></code></pre></div><p>We will also add the configuration required for the toast library.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VueToast</span> <span class="nx">from</span> <span class="s2">&#34;vue-toast-notification&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;vue-toast-notification/dist/theme-default.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">//  ...other code
</span></span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VueToast</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">position</span><span class="o">:</span> <span class="s2">&#34;top-right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Now, create a new file called <code>db.js</code> in your project root folder.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">firebase</span> <span class="nx">from</span> <span class="s2">&#34;firebase/app&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;firebase/firestore&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Get a Firestore instance
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">db</span> <span class="o">=</span> <span class="nx">firebase</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">initializeApp</span><span class="p">({</span> <span class="nx">projectId</span><span class="o">:</span> <span class="s2">&#34;fwtr-fwtr&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">firestore</span><span class="p">();</span>
</span></span></code></pre></div><p>Replace <code>fwtr-fwtr</code> with your own project name - you would have provided the project name in the Firebase console.</p>
<h2 id="home-page">Home Page</h2>
<p>Let&rsquo;s get back to our client app for a minute. First, let&rsquo;s include a stylesheet - because, we (most of us) don&rsquo;t live in caves no more.</p>
<p>Edit <code>/public/index.html</code>. Include the following statement to include an awesome library that automates your styling.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- Code --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/chota@latest&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">&lt;!-- Code --&gt;</span>
</span></span></code></pre></div><p><code>Chota.css</code> is a minimal CSS framework that does not require you to &ldquo;class&rdquo; each and every element. It also provides a minimal grid and commonly used styled components.</p>
<p>Next, change the home page a bit to keep things interesting. Edit <code>src/views/Home.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">nav</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav-left&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;brand&#34;</span><span class="p">&gt;</span>Fwtr<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav-right&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;nav-link&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/tweets&#34;</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;user.uid&#34;</span><span class="p">&gt;</span>Tweets<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/signup&#34;</span> <span class="na">v-else</span><span class="p">&gt;</span>Signup<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/login&#34;</span><span class="p">&gt;</span>User<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>?<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">router-view</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapState</span><span class="p">([</span><span class="s2">&#34;user&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>We will also include some style classes but those have been ignored here for the sake of brevity - see <a href="https://github.com/techformist/fwtr/blob/master/src/App.vue">App.vue in the repo</a>.</p>
<p>Add code for router <code>/src/router/index.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VueRouter</span> <span class="nx">from</span> <span class="s2">&#34;vue-router&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Home</span> <span class="nx">from</span> <span class="s2">&#34;../views/Home.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VueRouter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Home&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="nx">Home</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/about&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;About&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// route level code-splitting
</span></span></span><span class="line"><span class="cl">    <span class="c1">// this generates a separate chunk (about.[hash].js) for this route
</span></span></span><span class="line"><span class="cl">    <span class="c1">// which is lazy-loaded when the route is visited.
</span></span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;about&#34; */</span> <span class="s2">&#34;../views/About.vue&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/tweets&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Tweets&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Tweets.vue&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/signup&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Signup&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Signup.vue&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/login&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Login&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Login.vue&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VueRouter</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;history&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">routes</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">router</span><span class="p">;</span>
</span></span></code></pre></div><p>..and add some code to the store -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vuex</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">user</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setUser</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">user</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;updated state&#34;</span><span class="p">,</span> <span class="nx">state</span><span class="p">.</span><span class="nx">user</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">actions</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>You should have something like this by now..</p>
<p><img loading="lazy" src="/2020/fwtr-home.jpg" type="" alt="fwtr-home"  /></p>
<h2 id="authentication">Authentication</h2>
<p>A good fwtr application cannot exist without users who can shout everyone down and argue on significant pointless stuff. Enable users and authentication with the click or two in Firebase and Vue.</p>
<p>In your Firebase home page, select <code>Authentication</code> on the left tab bar. Navigate to <code>Sign-in Method</code> and enable authentication method. I have selected <code>Email/Password</code> as the only sign-in provider.</p>
<p><img loading="lazy" src="/2020/firestore-auth.jpg" type="" alt="firestore-auth"  /></p>
<p>Back to your client app, let&rsquo;s build registration and login functionality in the <code>login</code> page.</p>
<p>Create a <code>Signup</code> page at <code>/src/views/Signup.vue</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;justify: center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-3-md&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card cardycard row is-center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">h2</span>
</span></span><span class="line"><span class="cl">            <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold; padding-bottom:1em; padding-top:1em;&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            Sign up
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;login&#34;</span><span class="p">&gt;</span>Email<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;login&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;email&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;password&#34;</span><span class="p">&gt;</span>Password<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;userid&#34;</span><span class="p">&gt;</span>User Id<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;userid&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;userid&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button primary&#34;</span> <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">stop</span><span class="o">=</span><span class="s">&#34;register&#34;</span><span class="p">&gt;</span>Sign up<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-3-md&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">db</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;../db&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">firebase</span> <span class="nx">from</span> <span class="s2">&#34;firebase&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span><span class="p">,</span> <span class="nx">mapMutations</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">email</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">password</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">userid</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">login</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapMutations</span><span class="p">([</span><span class="s2">&#34;setUser&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">register</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">provider</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">firebase</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">GoogleAuthProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">firebase</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">auth</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">createUserWithEmailAndPassword</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="kr">await</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">updateProfile</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">displayName</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">userid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;user: &#34;</span><span class="p">,</span> <span class="nx">user</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">setUser</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">email</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">name</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">uid</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">uid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">refreshToken</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">refreshToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">displayName</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$toast</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="s2">&#34;Signed up!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$router</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">&#34;/tweets&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$toast</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>We collect user email, password and display name (no validation!), and passing it across to Firestore to create the user in our system.</p>
<p>You can start clicking around and see the users getting created provided you create blank <code>vue</code> files for all the other links outlined in <code>App.vue</code>.</p>
<p>Next create a new file called <code>/src/views/Login.vue</code> and add code to create a couple of fields -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;justify: center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-3-md&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card cardycard row is-center&#34;</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;!user.uid&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">h2</span>
</span></span><span class="line"><span class="cl">            <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold; padding-bottom:1em; padding-top:1em;&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            Login
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;login&#34;</span><span class="p">&gt;</span>Email<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;login&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;email&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">label</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;password&#34;</span><span class="p">&gt;</span>Password<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">input</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;password&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button primary&#34;</span> <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">stop</span><span class="o">=</span><span class="s">&#34;doLogin&#34;</span><span class="p">&gt;</span>Login<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card cardycard row is-center&#34;</span> <span class="na">v-else</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>You are already logged in!<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button primary&#34;</span> <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">stop</span><span class="o">=</span><span class="s">&#34;doLogout&#34;</span><span class="p">&gt;</span>Logout<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-3-md&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">db</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;../db&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">firebase</span> <span class="nx">from</span> <span class="s2">&#34;firebase&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapMutations</span><span class="p">,</span> <span class="nx">mapState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">email</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">password</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">login</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapState</span><span class="p">([</span><span class="s2">&#34;user&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapMutations</span><span class="p">([</span><span class="s2">&#34;setUser&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">doLogout</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">setUser</span><span class="p">({});</span>
</span></span><span class="line"><span class="cl">        <span class="kr">await</span> <span class="nx">firebase</span><span class="p">.</span><span class="nx">auth</span><span class="p">().</span><span class="nx">signOut</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">doLogin</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">provider</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">firebase</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">GoogleAuthProvider</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="p">{</span> <span class="nx">user</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">firebase</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">auth</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="nx">signInWithEmailAndPassword</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">password</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">setUser</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">email</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">name</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">uid</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">uid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">refreshToken</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">refreshToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">displayName</span><span class="o">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$toast</span><span class="p">.</span><span class="nx">success</span><span class="p">(</span><span class="s2">&#34;Logged in!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$router</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">&#34;/tweets&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$toast</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>You should be able to login with users created through the signup page.</p>
<p>Let&rsquo;s secure <code>tweets</code> on Firestore. Navigate to <code>Firebase Console</code> &gt; <code>Cloud Firestore</code>. Click on <code>Rules</code> tab on main page. We will replace the default rules to allow only authenticated users to interact with our app.</p>
<pre tabindex="0"><code>rules_version = &#39;2&#39;;
service cloud.firestore {
  match /databases/{database}/documents {

     match /tweets/{tweet} {
      allow create:
      	if request.auth != null &amp;&amp; request.auth.uid == request.resource.data.uid

    	allow update, delete:
      	if request.auth != null &amp;&amp; request.auth.uid == resource.data.uid
      allow read:
      	if true
    }
  }

}
</code></pre><p>All we have done here is to -</p>
<ul>
<li>enable authenticated users to read / create tweets</li>
<li>enable only owners to delete or update tweets</li>
</ul>
<p>With a couple of clicks on the Firebase console, and some copy/paste magic, we have created a secure app with full email/password authentication (incl. confirmation email and everything)!</p>
<h2 id="start-tweeting">Start Tweeting</h2>
<p>Create <code>/src/views/Tweet.vue</code> and include the below code -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold;&#34;</span><span class="p">&gt;</span>Tweets<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row is-center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-10 col-8-md&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">form</span>
</span></span><span class="line"><span class="cl">          <span class="na">class</span><span class="o">=</span><span class="s">&#34;row is-right&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="err">@</span><span class="na">submit</span><span class="err">.</span><span class="na">prevent</span><span class="o">=</span><span class="s">&#34;postTweet()&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;user.uid&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">input</span>
</span></span><span class="line"><span class="cl">            <span class="na">name</span><span class="o">=</span><span class="s">&#34;tweetin&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;tweetin&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;What&#39;s happening?&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-2&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">              <span class="na">class</span><span class="o">=</span><span class="s">&#34;button primary outline&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">type</span><span class="o">=</span><span class="s">&#34;submit&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="err">@</span><span class="na">click</span><span class="err">.</span><span class="na">prevent</span><span class="o">=</span><span class="s">&#34;postTweet()&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              Tweet
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;card tweetcard col-10 col-8-md&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(tweet, index) in tweets&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-6 is-left&#34;</span><span class="p">&gt;</span>@{{ tweet.uname }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-6 is-right&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color: grey; font-size: 80%&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            {{ tweet.createdAt ? new Date(tweet.createdAt).toUTCString() : &#34;&#34; }}
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12 is-left&#34;</span><span class="p">&gt;</span>{{ tweet.message }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;col-12 is-right&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">              <span class="na">class</span><span class="o">=</span><span class="s">&#34;button icon clear&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;deleteTweet(tweet.id)&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;tweet.uid == user.uid&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">img</span>
</span></span><span class="line"><span class="cl">                <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://icongr.am/feather/trash-2.svg?size=16&amp;amp;color=amber&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">alt</span><span class="o">=</span><span class="s">&#34;del&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button icon clear&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;postTweet(tweet.message)&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">img</span>
</span></span><span class="line"><span class="cl">                <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://icongr.am/feather/refresh-ccw.svg?size=16&amp;amp;color=#e1e1e1&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">alt</span><span class="o">=</span><span class="s">&#34;rt&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">db</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;../db&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tweets</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">        <span class="nx">tweetin</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapState</span><span class="p">([</span><span class="s2">&#34;user&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mounted</span><span class="p">()</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">postTweet</span><span class="p">(</span><span class="nx">twt</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">collection</span><span class="p">(</span><span class="s2">&#34;tweets&#34;</span><span class="p">).</span><span class="nx">add</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">message</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">twt</span> <span class="o">?</span> <span class="nx">twt</span> <span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">tweetin</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">uid</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">uid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">uname</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">createdAt</span><span class="o">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">twt</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">tweetin</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">deleteTweet</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">rec</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">db</span><span class="p">.</span><span class="nx">collection</span><span class="p">(</span><span class="s2">&#34;tweets&#34;</span><span class="p">).</span><span class="nx">doc</span><span class="p">(</span><span class="nx">id</span><span class="p">).</span><span class="k">delete</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$toast</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">firestore</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">tweets</span><span class="o">:</span> <span class="nx">db</span><span class="p">.</span><span class="nx">collection</span><span class="p">(</span><span class="s2">&#34;tweets&#34;</span><span class="p">).</span><span class="nx">orderBy</span><span class="p">(</span><span class="s2">&#34;createdAt&#34;</span><span class="p">,</span> <span class="s2">&#34;desc&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And.. that&rsquo;s it. You can celebrate and come back to do some testing whether your app indeed works.</p>
<p>Login with any user id, navigate to <code>Tweets</code>, and start tweeting. You can see others&rsquo; tweets and retweet them. You could also delete those <code>covfefe</code> tweets that seemed a good idea when you were in college.</p>
<h2 id="the-end">The End</h2>
<p>Hopefully you were entertained enough by this post? I always try to do my best with the jokes.. and of course, there was a bit of what Firestore could do.</p>
<p>The code for this project is available on <a href="https://github.com/techformist/fwtr">Github</a>.</p>
<p>Firestore is awesome, and Vue makes the entire developer experience delightful. Start weaving your magic with the combination and build great things.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Masonry Layout in Vuetify</title>
      <link>https://techformist.com/masonry-layout-vuetify/</link>
      <pubDate>Wed, 19 Aug 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/masonry-layout-vuetify/</guid>
      <description>&lt;p&gt;Vuetify is a God-send for lazy developers like me. Thanks to Vuetify I have all styles standardized, customise UI to my heart&amp;rsquo;s content and create an app that is ready to take on any device.&lt;/p&gt;
&lt;p&gt;But, Vuetify does not support masonry layout for your grid. Here&amp;rsquo;s a look at available options if you need masonry layouts in your projects.&lt;/p&gt;
&lt;h2 id=&#34;a-typical-vuetify-grid&#34;&gt;A Typical Vuetify Grid&lt;/h2&gt;
&lt;p&gt;The typical Vuetify grid is made up of rows and columns. Creating such a grid is quite simple.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Vuetify is a God-send for lazy developers like me. Thanks to Vuetify I have all styles standardized, customise UI to my heart&rsquo;s content and create an app that is ready to take on any device.</p>
<p>But, Vuetify does not support masonry layout for your grid. Here&rsquo;s a look at available options if you need masonry layouts in your projects.</p>
<h2 id="a-typical-vuetify-grid">A Typical Vuetify Grid</h2>
<p>The typical Vuetify grid is made up of rows and columns. Creating such a grid is quite simple.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span> People <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span> are <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span> awesome <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The components in a column can be anything. So, let&rsquo;s include images.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300px&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://picsum.photos/seed/1/200/100&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300px&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://picsum.photos/seed/1/200/100&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300px&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://picsum.photos/seed/1/200/100&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Making some small changes here and there, we get this.</p>
<p><img loading="lazy" src="/2020/vuetify-grid-standard.jpg" type="" alt="vuetify-grid"  /></p>
<p>Here&rsquo;s the <a href="https://codepen.io/techformist/pen/wvGeqdr">Codepen demonstrating a standard Vuetify grid</a>.</p>
<p>The grid is awesome and UI is responsive. Each column occupies 1/3rd the available screen space for large screens but expands to 100% of the screen in small view ports.</p>
<p>This is all good, until you meet images like you have in the above image. Each image can have its own height and may straight-up look uncool if you start restricting the height of images.</p>
<p>So, let&rsquo;s remove images from the artificial shackles of fixed height.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!--  ... --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;item.img&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="c">&lt;!--  ... --&gt;</span>
</span></span></code></pre></div><p><img loading="lazy" src="/2020/vuetify-grid-varying-image-size.jpg" type="" alt="vuetify-grid-varying-image-size"  /></p>
<p>The varying image heights does not show the full power of this grid and can have insane side-effects on otherwise sane people. You could alleviate the problem somewhat by using <code>height=100%</code> against the card and make all cards with uniform height, but the result is not a lot different in the above example.</p>
<p>The row height is equal to the height of the tallest image and you cross-over to the dark-side of using white space where it isn&rsquo;t required.</p>
<h2 id="what-is-masonry-layout">What is masonry layout?</h2>
<p>Masonry layout enables a combination of fixed width and full height for the components of a grid.</p>
<p><img loading="lazy" src="/2020/masonry-layout-vuetify.gif" type="" alt="masonry-layout"  /></p>
<p>As compared to a standard Vuetify grid, this offers full height when retaining the width. This is useful especially while displaying images/videos, but can have other use cases too.</p>
<p>Unfortunately there are no standard ways for varying row heights to build a masonry layout in Vuetify. But, there are a couple of ways to enable such layouts using additional libraries.</p>
<h2 id="creating-masonry-layout-in-vuetify">Creating masonry layout in Vuetify</h2>
<h4 id="use-v-masonry">Use v-masonry</h4>
<p><a href="https://github.com/shershen08/vue-masonry#readme">v-masonry</a> provides an easy way to add masonry layout to any Vue project. The library provides a Vue-specific wrapper around <a href="http://masonry.desandro.com/">masonry package</a>.</p>
<p>Start by installing <code>v-masonry</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm install vue-masonry --save
</span></span></code></pre></div><p>Include <code>v-masonry</code> reference in <code>main.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">App</span> <span class="nx">from</span> <span class="s2">&#34;./App.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vuetify</span> <span class="nx">from</span> <span class="s2">&#34;vuetify&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;vuetify/dist/vuetify.min.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;@mdi/font/css/materialdesignicons.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">VueMasonryPlugin</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue-masonry&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">productionTip</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuetify</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VueMasonryPlugin</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">render</span><span class="o">:</span> <span class="p">(</span><span class="nx">h</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">App</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="nx">$mount</span><span class="p">(</span><span class="s2">&#34;#app&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>Next, change the component to include <code>v-masonry</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-row</span> <span class="na">v-masonry</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(item, index) in items&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;index&#34;</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;100%&#34;</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;blue&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pt-1 pb-1&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-card-title</span><span class="p">&gt;</span>{{item.title}}<span class="p">&lt;/</span><span class="nt">v-card-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;item.img&#34;</span> <span class="err">@</span><span class="na">load</span><span class="o">=</span><span class="s">&#34;this.$redrawVueMasonry()&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-card-text</span><span class="p">&gt;</span>{{item.text}}<span class="p">&lt;/</span><span class="nt">v-card-text</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Note that we must call <code>this.$redrawVueMasonry()</code> to repaint the layout after the images are loaded since <code>v-img</code> is lazy loading.</p>
<p>I really liked <code>v-masonry</code>. I can happily use any tags anywhere, and just include a <code>v-masonry</code> to perform all the magic.</p>
<p>Here&rsquo;s a <a href="https://codesandbox.io/s/dry-wind-qkpsn">Codesandbox example</a>.</p>
<h4 id="use-vue-masonry-css">Use vue-masonry-css</h4>
<p>Another easy way to create masonry layout is by using <a href="https://github.com/paulcollett/vue-masonry-css">vue-masonry-css</a>. It&rsquo;s based on the same underlying <code>masonry</code> library - so don&rsquo;t expect anything to drastically change.</p>
<p>To start with <code>vue-masonry-css</code>, add the library to your project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">npm i --save vue-masonry-css
</span></span></code></pre></div><p>Change your component to use the new layout.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">masonry</span>
</span></span><span class="line"><span class="cl">      <span class="na">:gutter</span><span class="o">=</span><span class="s">&#34;{ default: &#39;30px&#39;, 700: &#39;15px&#39; }&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:cols</span><span class="o">=</span><span class="s">&#34;{ default: 3, 1000: 3, 700: 2, 500: 1 }&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in items&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;mt-2 mb-2&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">color</span><span class="o">=</span><span class="s">&#34;blue lighten-3&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">flat</span> <span class="na">dense</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;transparent&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{item.title}}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;item.img&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">masonry</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The big advantage of using <code>vue-masonry-css</code> is the &ldquo;automatic refresh&rdquo; of layout after the images/ cards are resized. Depending on your use case, you may or may not like the distinct <code>&lt;masonry&gt;&lt;/masonry&gt;</code> tag.</p>
<p>Here&rsquo;s a Codepen that demonstrates using Vuetify + <code>vue-masonry-css</code> -
<a href="https://codepen.io/techformist/full/ZEWyJOq">https://codepen.io/techformist/full/ZEWyJOq</a>.</p>
<h2 id="the-end">The End</h2>
<p>While masonry layout itself can be quite easily achieved regardless of your style library of choice, there were persistent issues with how cards can be resized in the layout when using Vuetify. I would have been delighted if Vuetify offered something like this out-of-the-box. <a href="https://github.com/vuetifyjs/vuetify/issues/4091">Vote for the idea</a> if you are in the same camp.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tooltip in Vuetify</title>
      <link>https://techformist.com/tooltip-vuetify/</link>
      <pubDate>Wed, 12 Aug 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/tooltip-vuetify/</guid>
      <description>&lt;p&gt;I am a big fan of Vuetify - it has far too many components available out of the box and makes my life as easy as it gets. But that does not prevent me from getting annoyed by small &amp;ldquo;stuff&amp;rdquo; that matter.&lt;/p&gt;
&lt;p&gt;Take tooltip as an example. Vuetify has &lt;code&gt;v-tooltip&lt;/code&gt; component to enable tooltips -&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;text-center d-flex align-center&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;v-tooltip&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;bottom&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;template&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;v-slot:activator&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;{ on, attrs }&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;v-btn&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;primary&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;dark&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;v-bind&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;attrs&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;v-on&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Button&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;v-btn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;span&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Yes, this is a button&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;span&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;v-tooltip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will result in the below UI -&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I am a big fan of Vuetify - it has far too many components available out of the box and makes my life as easy as it gets. But that does not prevent me from getting annoyed by small &ldquo;stuff&rdquo; that matter.</p>
<p>Take tooltip as an example. Vuetify has <code>v-tooltip</code> component to enable tooltips -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-center d-flex align-center&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-tooltip</span> <span class="na">bottom</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on, attrs }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span> <span class="na">dark</span> <span class="na">v-bind</span><span class="o">=</span><span class="s">&#34;attrs&#34;</span> <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span><span class="p">&gt;</span>Button<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>Yes, this is a button<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-tooltip</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This will result in the below UI -</p>
<p><img loading="lazy" src="/2020/vuetify-tool-tip.gif" type="" alt="vuetify-tool-tip"  /></p>
<p><a href="https://codepen.io/techformist/pen/wvGeYQZ">See this Codepen</a>.</p>
<p>But, 6 lines of code for every tooltip is just ridiculous. In an ideal world, I would want something like -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span> <span class="na">dark</span> <span class="na">tooltip</span><span class="o">=</span><span class="s">&#34;Yes, this is a button&#34;</span><span class="p">&gt;</span>Button<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Since we don&rsquo;t quite live in an ideal world yet, we can solve this problem on our own. I typically end up using one of the two solutions outlined below.</p>
<h2 id="solution-1-use-v-tooltip">Solution 1: Use <code>v-tooltip</code></h2>
<p><a href="https://github.com/Akryum/v-tooltip">v-tooltip</a> is a super easy-to-use tooltip solution that is used by the entire Vue universe.</p>
<p>Install the library -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save v-tooltip
</span></span></code></pre></div><p>Add plugin to Vue -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VTooltip</span> <span class="nx">from</span> <span class="s2">&#34;v-tooltip&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VTooltip</span><span class="p">);</span>
</span></span></code></pre></div><p>Use the tooltip -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">v</span><span class="o">-</span><span class="nx">button</span> <span class="nx">v</span><span class="o">-</span><span class="nx">tooltip</span><span class="o">=</span><span class="s2">&#34;&#39;Yes, this is a button&#39;&#34;</span><span class="o">&gt;&lt;</span><span class="err">/v-button&gt;</span>
</span></span></code></pre></div><p>It may also be a good idea to include the <a href="https://github.com/Akryum/v-tooltip#style-examples">default tooltip styles</a> - just in case.</p>
<p>While this is a good, easy-to-use solution - I don&rsquo;t really see myself using much. <code>v-tooltip</code> has a dependency on <code>loadash</code> and <code>popper.js</code>, which makes it a heavy-enough solution for general usage if I am not using the said libraries for other purposes.</p>
<h2 id="solution-2-create-your-own-tooltip-component">Solution 2: Create your own tooltip component</h2>
<p>Be pragmatic now - how many components will you include tooltip on?</p>
<ul>
<li>Images</li>
<li>Description for hard-to-understand buttons</li>
<li>List</li>
<li>??</li>
</ul>
<p>Instead of using third-party libraries, I just do the below nowadays -</p>
<ol>
<li>Include the full Vuetify tooltip in components like lists (which are hopefully small, anyway)</li>
<li>Create a reusable component for button, image cards etc. that wraps over tooltip</li>
</ol>
<p>(1) is fairly standard. Here&rsquo;s what you do for (2) - create your own component for a button.</p>
<p>Create a new component <code>ButtonTip</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-tooltip</span> <span class="na">bottom</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:activator</span><span class="o">=</span><span class="s">&#34;{ on, attrs }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">:color</span><span class="o">=</span><span class="s">&#34;color&#34;</span> <span class="na">v-bind</span><span class="o">=</span><span class="s">&#34;attrs&#34;</span> <span class="na">v-on</span><span class="o">=</span><span class="s">&#34;on&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;raiseClickEvent()&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">:color</span><span class="o">=</span><span class="s">&#34;iconcolor&#34;</span><span class="p">&gt;</span> {{ icon }} <span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        {{ text }}
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span> {{ tooltip }} <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-tooltip</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;ButtonTip&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">text</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">icon</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">color</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;primary&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">iconcolor</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;white&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">tooltip</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">eventname</span><span class="o">:</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="nb">String</span><span class="p">,</span> <span class="nx">required</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="k">default</span><span class="o">:</span> <span class="s2">&#34;clickyclicky&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">raiseClickEvent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">eventname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>If you want a button that displays tooltip in a component, you can now use -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ButtonTip</span>
</span></span><span class="line"><span class="cl">  <span class="na">text</span><span class="o">=</span><span class="s">&#34;Delete&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">icon</span><span class="o">=</span><span class="s">&#34;mdi-delete&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">tooltip</span><span class="o">=</span><span class="s">&#34;Delete&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">eventname</span><span class="o">=</span><span class="s">&#34;deleterec&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="err">@</span><span class="na">deleterec</span><span class="o">=</span><span class="s">&#34;deleteRecord()&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>What we are doing here is quite simple -</p>
<ol>
<li>Created a new Vue component that takes a bunch of props</li>
<li>Reused that component in other pages / components, passed on the props to achieve the desired result (in our case show a button with the relevant text, icon and tooltip)</li>
<li>When the button is clicked, we want to pass the <code>click event</code> back to the parent. That&rsquo;s the role of <code>eventname</code> and <code>@deleterec</code> (there will be shortcuts to do this, which I am sure I have missed)</li>
</ol>
<p><a href="https://codepen.io/techformist/pen/wvGeYQZ">See the same Codepen</a> to see <code>ButtonTip</code> in action.</p>
<h2 id="end">End</h2>
<p>Ain&rsquo;t life exciting?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Routing and Route Auth in Nuxt</title>
      <link>https://techformist.com/routing-auth-nuxt/</link>
      <pubDate>Wed, 05 Aug 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/routing-auth-nuxt/</guid>
      <description>&lt;p&gt;Nuxt enables automatic routing. All you need to do is -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the Nuxt app&lt;/li&gt;
&lt;li&gt;Create Vue pages under &lt;code&gt;pages&lt;/code&gt; root directory&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All pages will automatically be routes in the app - no need for a distinct Vue router definition.&lt;/p&gt;
&lt;h2 id=&#34;automatic-routes-in-nuxt&#34;&gt;Automatic Routes in Nuxt&lt;/h2&gt;
&lt;p&gt;Consider the below pages in an example app &lt;a href=&#34;https://my-app.com&#34;&gt;https://my-app.com&lt;/a&gt;-&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;/pages/index.vue&lt;/code&gt; for your home page =&amp;gt; maps to =&amp;gt; &lt;a href=&#34;https://my-app.com/&#34;&gt;https://my-app.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;/pages/contact.vue&lt;/code&gt; for your contact page =&amp;gt; maps to =&amp;gt; (&lt;a href=&#34;https://my-app.com/contact&#34;&gt;https://my-app.com/contact&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;/pages/customers.vue&lt;/code&gt; for to display customers =&amp;gt; maps to =&amp;gt; &lt;a href=&#34;https://my-app.com/customers&#34;&gt;https://my-app.com/customers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The directory structure in &lt;code&gt;pages&lt;/code&gt;..&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Nuxt enables automatic routing. All you need to do is -</p>
<ol>
<li>Create the Nuxt app</li>
<li>Create Vue pages under <code>pages</code> root directory</li>
</ol>
<p>All pages will automatically be routes in the app - no need for a distinct Vue router definition.</p>
<h2 id="automatic-routes-in-nuxt">Automatic Routes in Nuxt</h2>
<p>Consider the below pages in an example app <a href="https://my-app.com">https://my-app.com</a>-</p>
<ul>
<li>Create <code>/pages/index.vue</code> for your home page =&gt; maps to =&gt; <a href="https://my-app.com/">https://my-app.com/</a></li>
<li>Create <code>/pages/contact.vue</code> for your contact page =&gt; maps to =&gt; (<a href="https://my-app.com/contact">https://my-app.com/contact</a></li>
<li>Create <code>/pages/customers.vue</code> for to display customers =&gt; maps to =&gt; <a href="https://my-app.com/customers">https://my-app.com/customers</a></li>
</ul>
<p>The directory structure in <code>pages</code>..</p>
<pre tabindex="0"><code>pages/
--| index.vue
--| contact.vue
--| customers.vue
</code></pre><p>.. is equivalent to the below router file -</p>
<pre tabindex="0"><code>router: {
  routes: [
    {
      path: &#39;/&#39;,
      component: &#39;pages/index.vue&#39;,
    },
    {
      path: &#39;/contact&#39;,
      component: &#39;pages/contact.vue&#39;,
    },
    {
      path: &#39;/customers&#39;,
      component: &#39;pages/customers.vue&#39;,
    },
  ]
}
</code></pre><p>You can also have dynamic routes -</p>
<pre tabindex="0"><code>pages/
--| _id.vue
</code></pre><p>.. which is equivalent to =&gt; <a href="https://my-app.com/a1iDThis">https://my-app.com/a1iDThis</a></p>
<p>.. or nested routes..</p>
<pre tabindex="0"><code>pages/
--| customers/
-----| _id.vue
-----| index.vue
--| contact.vue
--| index.vue
</code></pre><p>.. which will generate =&gt; -</p>
<ul>
<li><a href="https://my-app.com/customers/c123">https://my-app.com/customers/c123</a></li>
<li><a href="https://my-app.com/customers/">https://my-app.com/customers/</a></li>
<li><a href="https://my-app.com/contact/">https://my-app.com/contact/</a></li>
<li><a href="https://my-app.com/">https://my-app.com/</a></li>
</ul>
<h2 id="using-route-middleware">Using Route Middleware</h2>
<p>Use middleware to perform dark magic on routes. You can protect routes and make them accessible only for authenticated users for example.</p>
<p>Create <code>middleware</code> directory in Nuxt root folder, and create a new file called <code>auth.js</code> therein.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="p">({</span> <span class="nx">store</span><span class="p">,</span> <span class="nx">redirect</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">client</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">store</span><span class="p">.</span><span class="nx">getters</span><span class="p">[</span><span class="s2">&#34;isLoggedIn&#34;</span><span class="p">])</span> <span class="k">return</span> <span class="nx">redirect</span><span class="p">(</span><span class="s2">&#34;/login&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The above code is doing two things -</p>
<ol>
<li>check whether current execution context is <code>client</code> (we don&rsquo;t want this check to happen on server - our cookie/session variables exist in client browser )</li>
<li>if current user is not logged in (provided by <code>isLoggedIn</code> ), redirect to <code>/login</code> page</li>
</ol>
<p>Tell Nuxt about this middleware - edit <code>nuxt.config.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ..
</span></span></span><span class="line"><span class="cl">  <span class="nx">middleware</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;auth&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ..
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>To use this middleware, just include the below code block in your page -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">middleware</span><span class="o">:</span> <span class="s2">&#34;auth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Now, Nuxt will redirect user to <code>/login</code> page if not already authenticated (i.e., <code>isLoggedIn</code> is <code>false</code>).</p>
<h2 id="override-default-behaviour-bring-your-own-router">Override Default Behaviour: Bring your own router</h2>
<p>You can always assume more control in routing if you are still getting adjusted to the idea of &ldquo;automatic routing&rdquo;, or simply have too many things going on during routing.</p>
<p>Use <a href="https://github.com/nuxt-community/router-module">router-module</a> in your Nuxt project.</p>
<ol>
<li>
<p>Install <code>npm i --save @nuxtjs/router</code></p>
</li>
<li>
<p>Add to build modules in <code>nuxt.config.js</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">buildModules</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;@nuxtjs/router&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div></li>
<li>
<p>Create <code>router.js</code> in your Nuxt project and include the &ldquo;normal&rdquo; router code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Router</span> <span class="nx">from</span> <span class="s2">&#34;vue-router&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Index</span> <span class="nx">from</span> <span class="s2">&#34;~/components/index&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Contact</span> <span class="nx">from</span> <span class="s2">&#34;~/components/contact&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Router</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">createRouter</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">new</span> <span class="nx">Router</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;history&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">routes</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="nx">component</span><span class="o">:</span> <span class="nx">Index</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/contact&#34;</span><span class="p">,</span> <span class="nx">component</span><span class="o">:</span> <span class="nx">Contact</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<h2 id="finis">Finis</h2>
<p>See <a href="https://nuxtjs.org/guide/routing/">more about Nuxt routing</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Structuring your Web App &amp; Nuxt Content Module</title>
      <link>https://techformist.com/structuring-web-app-nuxt/</link>
      <pubDate>Wed, 29 Jul 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/structuring-web-app-nuxt/</guid>
      <description>&lt;p&gt;I have always struggled with new projects. Don&amp;rsquo;t get me wrong - we live in exciting times and all that. But, there is this thing called &amp;ldquo;conundrum of choice&amp;rdquo;. There are just too many ways to get things done and the &amp;ldquo;next shiny thing&amp;rdquo; syndrome rears its head each and every darn time.&lt;/p&gt;
&lt;p&gt;I can safely say that I have never really achieved a standard flow to get things started. Why is that important did I hear you ask?&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have always struggled with new projects. Don&rsquo;t get me wrong - we live in exciting times and all that. But, there is this thing called &ldquo;conundrum of choice&rdquo;. There are just too many ways to get things done and the &ldquo;next shiny thing&rdquo; syndrome rears its head each and every darn time.</p>
<p>I can safely say that I have never really achieved a standard flow to get things started. Why is that important did I hear you ask?</p>
<ul>
<li>Starting a project is what I find more difficult than putting in the hours in the app, or completing the project</li>
<li>I explore available options far too deeply than what sane people do</li>
<li>I lose valuable time in &ldquo;starting things the right way&rdquo; rather than focusing on value-adding activities that deliver the goods</li>
</ul>
<p>So then, it is also safe to say I am forever searching for the one true framework/boiler plate for everything that a person needs to start a project. And that search, ladies and gentlemen, may have taken a big step towards conclusion.</p>
<p>Dramatic drumroll.. Nuxt.</p>
<p><img loading="lazy" src="/2020/nuxtjs-logo.jpg" type="" alt="nuxtjs_logo"  /></p>
<p>Before I go into why I am rick-rolling(?) Nuxt in all this, let us consider what goes in a web app for low-mid complexity web app.</p>
<h2 id="structure-of-the-modern-web-app">Structure of the Modern Web App</h2>
<p>I roll out products by myself. These are sometimes SaaS products, a few of them paid, and a lot of them created for small &amp; medium scale businesses that are not looking at rapid scaling by the end of the month.</p>
<p>My typical use case will consist of -</p>
<ol>
<li>Creating the app itself (insert favourite server-side and client-side framework here). This will consist of user auth incl. login/signup pages, app pages, and associated paraphernalia</li>
<li>Create a website that sings hymns about the app. Include a somewhat agreeable home page, pricing, help pages, T&amp;C pages etc.</li>
<li>Create a blog that can provide updates and do a dance or two for SEO</li>
</ol>
<p>While this is straight forward enough, I had days &amp; nights when I would have nightmares on what things go where, and how should I get stuff done quickly.</p>
<ul>
<li>SSR or Pre-render</li>
<li>Consistent styling across website and app</li>
<li>Easy to use, editor-friendly blogs</li>
<li>SEO friendly</li>
</ul>
<h2 id="meet-my-solutions">Meet My Solutions</h2>
<p>As I made pretty clear multiple times till now, I don&rsquo;t have a clear cookie-cutter solution. My go-to solution consisted of -</p>
<ol>
<li>Using a single page app (always an SPA)
<ul>
<li>Connect to backend through RESTful APIs / use web sockets / sessions</li>
<li>Use pre-render for pages with lot of content</li>
</ul>
</li>
<li>Use a static site generator for the larger site and blog</li>
<li>Go back and forth between components to get them all to work together</li>
</ol>
<p>Obviously, I could (or should) have steered clear of this mess by using server-rendered pages using templates (think Laravel, or numerous Node frameworks), I somehow did not quite take a firm step in that direction.</p>
<p>My technologies of choice to get things going had been -</p>
<ol>
<li><a href="https://vuejs.org/">Vue</a> - for SPA. <a href="https://nuxtjs.org/">Nuxt</a> for added SSR effect</li>
<li><a href="https://gohugo.io/">Hugo</a> - for static sites and blog. (OR) <a href="https://wordpress.org/">Wordpress</a> for sites &amp; blog if I want to feel like living on the edge</li>
</ol>
<p>I had gravitated towards using <a href="https://gridsome.org/">Gridsome</a> or <a href="https://saber.land">Saber</a> for sites/blog since then. They made it a lot easier to keep sites and the app consistent - for a while.</p>
<h2 id="my-new-favourite---nuxt">My New Favourite - Nuxt</h2>
<p>You may have seen Nuxt in my choice of technologies. So, what changed and why this this big intro that may overshadow the entire post?</p>
<p>Two words - <a href="https://content.nuxtjs.org/">Nuxt content</a>.</p>
<p>Nuxt has been my preferred tool when I need SSR, but I don&rsquo;t quite like that experience a whole lot. I take issue with -</p>
<ol>
<li>Increased hot-reload times during development. Yes, this matters for a person like me who makes far too many design mistakes</li>
<li>Added complexity of having to deal with server-side and client-side logic. Also a few libraries tend to become more ape-like with SSR (even with <code>&lt;client-only&gt;</code>)</li>
<li>Inconsistent, weird build behaviour - especially when I am using Vuetify/ Bootstrap-Vue. (Yes, I cannot reproduce those errors. Yes, I somehow manage to change config and make them go away)</li>
</ol>
<p>But, more recently I have taken a liking to the &ldquo;new life cycle of development&rdquo;.</p>
<ol>
<li>Nuxt for SPA</li>
<li>Nuxt for the entire site and blog</li>
</ol>
<p>Now I have -</p>
<ul>
<li>consistent styling</li>
<li>a common / consistent way of coding</li>
<li>less maintenance work</li>
</ul>
<p>What changed the game for me was the content module.</p>
<h2 id="why-content-module-is-awesome">Why Content Module is awesome?</h2>
<p>My first introduction to the content module was through <a href="https://nuxtjs.org/blog/creating-blog-with-nuxt-content/">this article on Nuxt blog</a> - published last month. Like all content on Internet, I bookmarked it and moved on with life.</p>
<p>It was only much later that I started a &ldquo;oh-so-cool&rdquo; project, and I looked out for anything new that could help me be a bit more productive. Boy, was I in for a surprise.</p>
<p>Nuxt content does one awesome thing - it sources the content during build and creates the pages that are SEO-friendly, SSR-friendly and can be delivered to the client at the blink of an eye.</p>
<ul>
<li>Create a content folder and put in your markdown files, JSON, CSV or XML files in there</li>
<li>Point Nuxt to content folder. Enable the module</li>
<li>Fetch your content in relevant pages</li>
<li>Sit back and start building</li>
</ul>
<p>In other words the content module bestows &ldquo;static site generation&rdquo; powers to your Nuxt app without actually making it a static site (nothing wrong with a static site per se).</p>
<p>So, now I could -</p>
<ol>
<li>Separate out content and presentation layers (greater reuse)</li>
<li>Use one framework for content heavy pages and my app</li>
<li>Choose SSR or SPA and do not make a fuss about overall site vs. app</li>
</ol>
<h2 id="getting-started">Getting Started</h2>
<p>Start your new nuxt project.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npx create-nuxt-app todo
</span></span></code></pre></div><p>Install content module.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm install @nuxt/content
</span></span></code></pre></div><p>Add module to your <code>nuxt.config</code> file -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;modules&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;@nuxt/content&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You are now ready to unleash your content. Create a <code>content</code> folder in root - you can add any subfolder or files to this folder.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">md</span> content
</span></span><span class="line"><span class="cl"><span class="k">cd</span> content
</span></span><span class="line"><span class="cl"><span class="k">md</span> blog
</span></span><span class="line"><span class="cl"><span class="k">cd</span> blog
</span></span><span class="line"><span class="cl">touch hello.md
</span></span></code></pre></div><p>Write in your awesome post in <code>hello.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: Hello World
</span></span><span class="line"><span class="cl">description: Here&#39;s an obligatory &#39;hello world&#39; post
</span></span><span class="line"><span class="cl">createdAt: 2020-01-01
</span></span><span class="line"><span class="cl">slug: hello
</span></span><span class="line"><span class="cl">tags:
</span></span><span class="line"><span class="cl">  <span class="k">-</span> test
</span></span><span class="line"><span class="cl">  <span class="k">-</span> another
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Hello World
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I just came here to say that.
</span></span></code></pre></div><p>Create a template that can render this markdown. Create a folder called <code>blog</code> under the root <code>pages</code> directory.</p>
<p>Create a new file <code>index.vue</code>. This file will be the layout to display our main blog content (e.g. our list of posts) - I am using Vuetify here, but you can use any style library.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;posts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-card</span>
</span></span><span class="line"><span class="cl">      <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">flat</span>
</span></span><span class="line"><span class="cl">      <span class="na">width</span><span class="o">=</span><span class="s">&#34;100%&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">height</span><span class="o">=</span><span class="s">&#34;150px&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-center pt-6 pb-6 mb-2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-h5 grey--text&#34;</span><span class="p">&gt;</span>Blog<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grey--text caption mt-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">nuxt-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grey--text&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">nuxt-link</span><span class="p">&gt;</span> / Blog
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;post in posts&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;post.dir&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-h4 font-weight-bold mb-6&#34;</span><span class="p">&gt;</span>{{ post.title }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grey--text caption mb-4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">span</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;post.createdAt&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey&#34;</span><span class="p">&gt;</span>mdi-calendar<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                {{ post.createdAt.substr(0, 10) }}
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              | Tags:
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">span</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(tag, i) in post.tags&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;i&#34;</span> <span class="na">justify</span><span class="o">=</span><span class="s">&#34;around&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">v-chip</span> <span class="na">dense</span> <span class="na">small</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-3&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mr-1&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  {{ tag }}
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">v-chip</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>{{ post.description }}<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">nuxt-link</span> <span class="na">:to</span><span class="o">=</span><span class="s">&#34;`/blog/${post.slug}`&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&gt;</span>Read more<span class="p">&lt;/</span><span class="nt">nuxt-link</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">asyncData</span><span class="p">({</span> <span class="nx">params</span><span class="p">,</span> <span class="nx">error</span><span class="p">,</span> <span class="nx">$content</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">$content</span><span class="p">(</span><span class="s1">&#39;blog&#39;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">deep</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}).</span><span class="nx">fetch</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Your discerning mind would have noted a few things here -</p>
<ol>
<li>We used <code>asyncData</code> in the page to retrieve content. This happens during build time. You are free to work your SEO magic here</li>
<li>You utilise the content read during <code>asyncData</code> like any other Vue variable</li>
<li>You did nothing additional other than a plan-ol&rsquo; Nuxt project - which is a good thing</li>
</ol>
<p>After all that hard work, your Blog is ready to roll.</p>
<p><img loading="lazy" src="/2020/nuxt-content-module-create-blog.jpg" type="" alt="nuxt-content-module-create-blog"  /></p>
<p>We did all this without leaving the framework that is also responsible for our app. So, the real question you should be asking now is &ldquo;have I reached app starter nirvana?&rdquo;.</p>
<h2 id="finis">Finis</h2>
<p>What do you think? What will you do for your next big app!?</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>The Magic of VueDraggable</title>
      <link>https://techformist.com/magic-vuedraggable/</link>
      <pubDate>Wed, 22 Jul 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/magic-vuedraggable/</guid>
      <description>&lt;p&gt;I had used VueDraggable in my projects before, but only sparingly. One of the recent experiences taught me just how cool it was!&lt;/p&gt;
&lt;h2 id=&#34;the-problem-of-drag--drop&#34;&gt;The Problem of Drag &amp;amp; Drop&lt;/h2&gt;
&lt;p&gt;There is no problem.. really.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You drag&lt;/li&gt;
&lt;li&gt;You drop&lt;/li&gt;
&lt;li&gt;And the world goes &amp;ldquo;yeah yeah yeah&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(.. &lt;a href=&#34;https://www.youtube.com/watch?v=F2AitTPI5U0&#34;&gt;use this tone&lt;/a&gt; - sorry, couldn&amp;rsquo;t resist)&lt;/p&gt;
&lt;p&gt;The problem manifests itself when that drag and drop can include any and all components. So, that&amp;rsquo;s effectively the modern web.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I had used VueDraggable in my projects before, but only sparingly. One of the recent experiences taught me just how cool it was!</p>
<h2 id="the-problem-of-drag--drop">The Problem of Drag &amp; Drop</h2>
<p>There is no problem.. really.</p>
<ol>
<li>You drag</li>
<li>You drop</li>
<li>And the world goes &ldquo;yeah yeah yeah&rdquo;</li>
</ol>
<p>(.. <a href="https://www.youtube.com/watch?v=F2AitTPI5U0">use this tone</a> - sorry, couldn&rsquo;t resist)</p>
<p>The problem manifests itself when that drag and drop can include any and all components. So, that&rsquo;s effectively the modern web.</p>
<p><a href="https://sortablejs.github.io/Vue.Draggable">VueDraggable</a> can make that problem go away in a nice way. You can almost drag any component to any component, and still live to tell that tale. Do remember though - we are talking about solving the problem of dragging external resources (like URLs, images or text ) on to our app (though a few libraries do that as well), but rather dealing with the problem of moving or copying a component of our app in &ldquo;some other&rdquo; (hopefully happier) place.</p>
<h2 id="get-started">Get Started</h2>
<p>Add the <code>vuedraggable</code> package to get started.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vuedraggable
</span></span></code></pre></div><p>You can now use <code>&lt;draggable&gt;</code> component.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- almost anything --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Anything that you want to drag and drop will be contained within <code>draggable</code>. Let&rsquo;s see a few examples to understand more.</p>
<h2 id="simple-drag--drop">Simple Drag &amp; Drop</h2>
<p>The beauty of <code>vuedraggable</code> is that you can drag and drop anything. Let&rsquo;s try out with something simple first - text.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;items&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-col</span>
</span></span><span class="line"><span class="cl">    <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;(item, index) in items&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {{item.title}}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Write some Javascript..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">vuetify</span><span class="o">:</span> <span class="k">new</span> <span class="nx">Vuetify</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">items</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;abc&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;ghi&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;jkl&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;mno&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;def&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//
</span></span></span><span class="line"><span class="cl">  <span class="p">}),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>And you are done!</p>
<p><img loading="lazy" src="/2020/vuedraggable-text.gif" type="" alt="vuedraggable-text"  /></p>
<p>Vuedraggable takes care of identifying the elements, finding their boundaries, and pushing the elements to the right place after the drag &amp; drop.</p>
<h2 id="drag--drop-from-one-group-to-the-other">Drag &amp; drop from one group to the other</h2>
<p>Kanban boards are all the rage these days (huh.. kids). How about designing your own Kanban board? Just use Vuedraggable!</p>
<p>See the below example using Vuetify to create the grid and for some basic styling.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color:red; font-weight:bold; padding-bottom:30px&#34;</span><span class="p">&gt;</span>Planned<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;planned&#34;</span> <span class="na">group</span><span class="o">=</span><span class="s">&#34;kanban&#34;</span> <span class="na">tag</span><span class="o">=</span><span class="s">&#34;ul&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">li</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in planned&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              {{item.title}}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color:red; font-weight:bold; padding-bottom:30px&#34;</span><span class="p">&gt;</span>In Progress<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;inprogress&#34;</span> <span class="na">group</span><span class="o">=</span><span class="s">&#34;kanban&#34;</span> <span class="na">tag</span><span class="o">=</span><span class="s">&#34;ul&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">li</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in inprogress&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              {{item.title}}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color:red; font-weight:bold; padding-bottom:30px&#34;</span><span class="p">&gt;</span>Done<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;done&#34;</span> <span class="na">group</span><span class="o">=</span><span class="s">&#34;kanban&#34;</span> <span class="na">tag</span><span class="o">=</span><span class="s">&#34;ul&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">li</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in done&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;font-weight:bold;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              {{item.title}}
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-app</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Add Javascript..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">vuetify</span><span class="o">:</span> <span class="k">new</span> <span class="nx">Vuetify</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">planned</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;abc&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;ghi&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;jkl&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;mno&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;def&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">inprogress</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">6</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;123&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">7</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;345&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">done</span><span class="o">:</span> <span class="p">[{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">8</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;!@#&#34;</span> <span class="p">}],</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//
</span></span></span><span class="line"><span class="cl">  <span class="p">}),</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>You will get to this result in no time..</p>
<p><img loading="lazy" src="/2020/vuedraggable-text-group.gif" type="" alt="vuedraggable-text-group"  /></p>
<p>All we did was to specify a <code>group</code> name that should remain the same for all elements in the same group.</p>
<p>Also, did you take note that I used a different tag here?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;inprogress&#34;</span> <span class="na">group</span><span class="o">=</span><span class="s">&#34;kanban&#34;</span> <span class="na">tag</span><span class="o">=</span><span class="s">&#34;ul&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">draggable</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>You can specify any tag to wrap around your draggable elements using <code>tag</code>, it is <code>div</code> by default. Take care to see that the draggable elements (the elements with <code>for</code> loop) are direct descendants of <code>&lt;draggable&gt;</code> element.</p>
<p><a href="https://codepen.io/techformist/pen/GRZEOoa">Try it out on Codepen</a>.</p>
<h2 id="drag--drop-images">Drag &amp; drop images</h2>
<p>How about dragging and dropping images?</p>
<p><img loading="lazy" src="/2020/vuedraggable-images.gif" type="" alt="vuedraggable-images"  /></p>
<p>Images are just as easy as text.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">draggable</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;items&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;row&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">sm</span><span class="o">=</span><span class="s">&#34;4&#34;</span> <span class="na">v-for</span><span class="o">=</span><span class="s">&#34;item in items&#34;</span> <span class="na">:key</span><span class="o">=</span><span class="s">&#34;item.id&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;.item&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mt-2 mb-2 item&#34;</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;grey lighten-4&#34;</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-toolbar</span> <span class="na">flat</span> <span class="na">dense</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;transparent&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;font-weight-bold&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          {{item.title}}
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-toolbar</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-img</span> <span class="na">:src</span><span class="o">=</span><span class="s">&#34;item.img&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;300px&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">v-img</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">draggable</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;&lt;/</span><span class="nt">v-container</span><span class="p">&gt;</span>
</span></span></code></pre></div><p><a href="https://codepen.io/techformist/pen/mdPwBZa">Here&rsquo;s the Codepen</a>.</p>
<h2 id="how-about-alternatives">How about alternatives?</h2>
<p>VueDraggable is based on <code>sortable</code> library and is fairly well documented and quite popular. You should be in safe hands for the most part. There are other libraries like -</p>
<ul>
<li><a href="https://github.com/sagalbot/vue-sortable">vue-sortable</a>, also based on the <code>sortable</code> library but not as widely used</li>
<li><a href="https://www.npmjs.com/package/vue-drag-sortable">vue-drag-sortable</a></li>
<li><a href="https://github.com/Astray-git/vue-dragula">Vue-Dragula</a>: Based on Dragula, does not seem to be actively maintained anymore</li>
</ul>
<p>However, do note a limitation of <code>sortablejs</code> (and VueDraggable) - it makes use of native HTML5 API for drag &amp; drop and is subject to its constraints. You will notice this if you need to take control of every event or enable advanced functions during drag &amp; drop.</p>
<p>To go beyond HTML5 API, have a look at one of the alternatives below.</p>
<ul>
<li><a href="https://github.com/jbaysolutions/vue-grid-layout">vue-grid-layout</a>: Certainly one of the more powerful libraries for drag &amp; drop. Quite powerful, actively maintained and it just rocks</li>
<li><a href="https://github.com/rlemaigre/Easy-DnD">vue-easy-dnd</a>: This library is fairly new but gaining rapid popularity</li>
<li><a href="https://github.com/kutlugsahin/vue-smooth-dnd">Vue-smooth-dnd</a>: offers more functionality, not constrained by HTML5 API</li>
</ul>
<p>While the above libraries are powerful, they also need quite a bit more coding to get things going. For example, <code>vue-grid-layout</code> needs the exact points in x/y-axes, and element height/width.</p>
<h2 id="finis">Finis</h2>
<p>VueDraggable is a super-friendly, and easy-to-use library. You can do much much more with VueDraggable like cloning elements, drag &amp; drop nested elements.</p>
<p>See <a href="https://sortablejs.github.io/Vue.Draggable">more examples of what VueDraggable can do</a>. Use it in your own projects and go crazy!</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Working with Local Storage in Vue</title>
      <link>https://techformist.com/local-storage-vue/</link>
      <pubDate>Wed, 15 Jul 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/local-storage-vue/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s an high-level overview of local storage, popular options to get started on local storage in Vue, and how to choose an option that works for you.&lt;/p&gt;
&lt;h2 id=&#34;local-storage-and-browsers&#34;&gt;Local Storage and Browsers&lt;/h2&gt;
&lt;p&gt;Browsers are windows to your web application. While it is easier to manage data access on your own servers, super secure and all, getting users to connect to the server for each and every task can be tiring and lead to a bad user experience. These are some use cases that highlight such problems -&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s an high-level overview of local storage, popular options to get started on local storage in Vue, and how to choose an option that works for you.</p>
<h2 id="local-storage-and-browsers">Local Storage and Browsers</h2>
<p>Browsers are windows to your web application. While it is easier to manage data access on your own servers, super secure and all, getting users to connect to the server for each and every task can be tiring and lead to a bad user experience. These are some use cases that highlight such problems -</p>
<ul>
<li>Save data on a form that is not submitted yet</li>
<li>Save device-specific user preferences that are transient in nature (e.g. show image in portrait mode on mobile phones)</li>
<li>Provide limited browsing capabilities and user interaction even when user is offline (using PWA for example)</li>
</ul>
<p>The obvious answers to such problems is to enable a &ldquo;database&rdquo; accessible by the web apps but present on the local device. But, this is made complicated by the nature of the web.</p>
<p>In the days of the old wild west, the only solution to storing local data was through &ldquo;cookies&rdquo;. Any and all local data including authentication, user preferences and what not were stored as key-value pairs in a file specific to the domain and managed by the browser. Cookies were good, but -</p>
<ol>
<li>Had limited storage</li>
<li>Had to be included in every server request - irrelevant for non-auth data</li>
<li>A few more problems of the old web that we are too afraid to get into</li>
</ol>
<p>HTML5 upped the ante in the space by standardizing the way to store and manage data locally - affectionately called &ldquo;<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">local storage</a>&rdquo;. While local storage continues to store name-value pairs in some remote files, it has a few advantages over the older solution -</p>
<ol>
<li>No sending stuff to server. Local store is for local usage (unless you decide to do that for your own purposes)</li>
<li>Higher storage limit - 5 MB for each domain + protocol</li>
<li>Did I mention that the APIs for local store are standard across browsers?</li>
</ol>
<p>All you need to do to access the store is write couple of lines of Javascript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">localStorage</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="s2">&#34;hello world&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nx">localStorage</span><span class="p">.</span><span class="nx">count</span> <span class="o">=</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">count</span> <span class="o">?</span> <span class="o">+</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">count</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`Important message (displayed </span><span class="si">${</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">count</span><span class="si">}</span><span class="sb"> times): </span><span class="si">${</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">`</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>(see <a href="https://codepen.io/prashanth1k/pen/wvMNNRV">Codepen</a>)</p>
<p>In the above example <code>count</code> would have reset every time the browser is refreshed - unless you stored it server-side or persisted it in cookies. With <code>localStorage</code> - <code>count</code> is stored in the local store, not sent to the server with each request, and fully accessible by the application. You can open the console to see <code>count</code> being incremented with each browser refresh.</p>
<h2 id="vue-and-local-storage">Vue and Local Storage</h2>
<p>Vue is just Javascript.</p>
<p>I know it is difficult to believe that since it seems so magical, but it is true. Vue brings reactivity, a great way to manage user experience, and easier way to manage states.</p>
<p>There are varying degrees of.. ahem, sophistication you can bring to work with local storage in Vue.</p>
<h3 id="1-directly-work-with-localstorage-in-vue">1. Directly Work with LocalStorage in Vue</h3>
<p>Dealing with local storage is simple in Vue. But, before we get into that let&rsquo;s see how &ldquo;normal&rdquo; code looks like.</p>
<p>Here&rsquo;s a simple html template -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">head</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">link</span> <span class="nx">rel</span><span class="o">=</span><span class="s2">&#34;stylesheet&#34;</span> <span class="nx">href</span><span class="o">=</span><span class="s2">&#34;https://unpkg.com/mvp.css&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/head&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">body</span> <span class="nx">style</span><span class="o">=</span><span class="s2">&#34;text-align:center;&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">h2</span><span class="o">&gt;</span><span class="nx">Simple</span> <span class="nx">Vue</span> <span class="o">+</span> <span class="nx">Local</span> <span class="nx">Storage</span><span class="o">&lt;</span><span class="err">/h2&gt;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="s2">&#34;container&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Hello</span><span class="p">,</span> <span class="p">{{</span><span class="nx">name</span><span class="p">}}</span><span class="o">!</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">You</span> <span class="nx">have</span> <span class="nx">seen</span> <span class="k">this</span> <span class="p">{{</span><span class="nx">count</span><span class="p">}}</span> <span class="nx">times</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">If</span> <span class="nx">you</span> <span class="nx">remember</span> <span class="nx">across</span> <span class="nx">sessions</span><span class="p">,</span> <span class="nx">you</span> <span class="nx">would</span> <span class="nx">have</span> <span class="nx">seen</span> <span class="k">this</span> <span class="p">{{</span><span class="nx">countstore</span><span class="p">}}</span> <span class="nx">times</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="err">/div&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/body&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/html&gt;</span>
</span></span></code></pre></div><p>And.. the Vue script..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#container&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;world&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">countstore</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>You will see this beautiful page.</p>
<p><img loading="lazy" src="/2020/vue-vanilla.jpg" type="" alt="vue-vanilla"  /></p>
<p>You can see however, that the count is lost each time the browser is refreshed.</p>
<p>Let us improve it by introducing <code>localStorage</code> (with a slightly different syntax as compared to the previous example).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#container&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;world&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">,</span> <span class="o">+</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">countstore</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>(See <a href="https://codepen.io/prashanth1k/pen/qBbgeRB">Codepen</a>)</p>
<p>Now each time you refresh you continue seeing the same value in <code>count</code> but <code>countstore</code> is persistent and increments with each view.</p>
<p><img loading="lazy" src="/2020/vue-simple-local-storage.jpg" type="" alt="vue-vanilla"  /></p>
<h3 id="2-use-a-package-to-manage-variables-in-components">2. Use a Package to Manage Variables in Components</h3>
<p>While Vue works just fine with the above option, using variables within Vue components get mighty inconvenient. A cleaner approach may be to use one of the packages that abstract away the local storage.</p>
<p>This being Javascript we will ignore the fact that the &ldquo;inconvenience&rdquo; can be solved by just including a prototype. Instead we reward the work of others who have already solved the problem in the past.</p>
<p>Let us see an example by using <a href="https://www.npmjs.com/package/vue-ls">Vue LS</a>, a popular package that makes the local store available through a Vue prototype.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VueStorage</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#container&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;world&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">))</span> <span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">,</span> <span class="o">+</span><span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">countstore</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;countstore&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>(See <a href="https://codepen.io/prashanth1k/pen/BajbapN">Codepen</a>).</p>
<p>The results are same as earlier. You can use Vue-LS (or a comparable alternative) in components, mixins, or any other part of Vue.</p>
<h3 id="3-use-vuex--local-store">3. Use Vuex + Local Store</h3>
<p>Vuex is the state management library for Vue. It vastly simplifies a central storage location that is accessible from any Vue component.</p>
<p>Rewriting our script using Vuex..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">store</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;world&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">countstore</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">incrCountStore</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">state</span><span class="p">.</span><span class="nx">countstore</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#container&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">store</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">commit</span><span class="p">(</span><span class="s2">&#34;incrCountStore&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">countstore</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">countstore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">count</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>It is very much possible to use <code>vue-ls</code> (or comparable solutions) with Vuex. All you need to do is to keep getting and setting variables every time a state is changed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// get state
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">state</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">count</span><span class="o">:</span> <span class="p">()=&gt;</span><span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&#34;count&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// well, typically you set `count` in Vuex at app startup
</span></span></span><span class="line"><span class="cl">  <span class="c1">// by retrieving value from localstore
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// set state - typically done with each mutation
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">mutations</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setCount</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">count</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">state</span><span class="p">.</span><span class="nx">count</span> <span class="o">=</span> <span class="nx">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">$ls</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s2">&#34;count&#34;</span><span class="p">,</span> <span class="nx">count</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span></code></pre></div><p>As you can imagine, setting tens (or hundreds?) of variables is not quite a fun thing to do.</p>
<p>Again, this is Javascript and someone have already solved this problem for us. Enter solutions like <code>vuex-persistedstate</code> and <code>vuex-persist</code> that automatically synchronize vuex states with local storage.</p>
<p>We will look at an example of using <a href="https://github.com/robinvdvleuten/vuex-persistedstate">vuex-persistedstate</a>. Just add a single line to include the <code>createPersistedState</code> plugin while defining the store. Rest of the code in the Vuex example will remain as-is.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">store</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">createPersistedState</span><span class="p">()],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#container&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">store</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">//   ...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Refresh the browser and you see <code>countstore</code> being persisted across sessions.</p>
<h2 id="which-option-is-better">Which option is better?</h2>
<p>We saw three ways of working with local store in Vue. You may start from the simplest option or choose one that fits your project.</p>
<ul>
<li>If I am working with simple Vue frontend and don&rsquo;t quite have anything to do with managing state, I would use a solution like <code>vue-ls</code>. It makes things easier and everyone loves tidy code.</li>
<li>If I am working with Vuex, I invariably gravitate towards <code>vuex-persist</code>. I tend to work with states that need to persist across sessions - storing state in local store gives me a &ldquo;clean&rdquo; way to get stuff done quickly</li>
</ul>
<p>While I am not saying everyone should choose Vuex, I encourage everyone to carefully evaluate their situation and <em>always</em> choose the hammer that is Vuex (<code>/s</code>). Pair it with one of the &lsquo;store persistence&rsquo; solutions and you will <em>never</em> regret getting into web development :).</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create a simple app using Vue from CDN</title>
      <link>https://techformist.com/use-vue-from-url-simple-app/</link>
      <pubDate>Wed, 27 May 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/use-vue-from-url-simple-app/</guid>
      <description>&lt;p&gt;I had to work on a MVP where there were specific instructions to use Vue directly from a CDN. The Vue build available in this way is also called UMD (Universal Module Definition) build since you can use Vue from anywhere and the project does not need specific setup to build and package your code. Here&amp;rsquo;s a demo of how a simple Vue setup from CDN can be used for quick demo projects.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I had to work on a MVP where there were specific instructions to use Vue directly from a CDN. The Vue build available in this way is also called UMD (Universal Module Definition) build since you can use Vue from anywhere and the project does not need specific setup to build and package your code. Here&rsquo;s a demo of how a simple Vue setup from CDN can be used for quick demo projects.</p>
<p>But, why use Vue from CDN? There may be a few cases where such an arrangement can help-</p>
<ol>
<li>demo simple functionality (and do not want to spend time on setup)</li>
<li>create quick MVPs that can be demonstrated using simple HTML and JS files</li>
<li>make everything portable; enable others to easily change stuff without the full Vue setup</li>
</ol>
<p>Rather than building a simple &ldquo;hello world&rdquo;, we will incorporate two components and router, and axios to call external services - and get all this working within a single HTML page and a couple of JS files.</p>
<p><img loading="lazy" src="/2020/simple-vue-html-axios-demo.gif" type="" alt="simple-vue-html-axios-demo"  /></p>
<h2 id="setup">Setup</h2>
<p>There is nothing to setup when using Vue from CDN. Create a new <code>index.html</code> page and include Vue (and Vue router).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>The Coolest Vue - Axios Demo<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/npm/vue@2.6.0&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/vue-router@2.0.0&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    Sample page
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>You are all set to start coding.</p>
<h2 id="add-styles-and-footer">Add Styles and Footer</h2>
<p>Add Bootstrap for future styling - we will use it for nav pane, and restrict ourselves to simple styles elsewhere.</p>
<p>Also introduce a footer while we are at it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>The Coolest Vue - Axios Demo<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://cdn.jsdelivr.net/npm/vue@2.6.0&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/vue-router@2.0.0&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">link</span>
</span></span><span class="line"><span class="cl">      <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nc">section</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">margin-top</span><span class="p">:</span> <span class="mi">2</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;wrapper&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">footer</span>
</span></span><span class="line"><span class="cl">        <span class="na">style</span><span class="o">=</span><span class="s">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">          background-color: rgb(236, 236, 236);
</span></span></span><span class="line"><span class="cl"><span class="s">          position: fixed;
</span></span></span><span class="line"><span class="cl"><span class="s">          width: 100%;
</span></span></span><span class="line"><span class="cl"><span class="s">          font-size: 80%;
</span></span></span><span class="line"><span class="cl"><span class="s">          margin-top: 5em;
</span></span></span><span class="line"><span class="cl"><span class="s">          padding-top: 5px;
</span></span></span><span class="line"><span class="cl"><span class="s">          padding-bottom: 5px;
</span></span></span><span class="line"><span class="cl"><span class="s">          bottom: 0;
</span></span></span><span class="line"><span class="cl"><span class="s">        &#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;container&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">i</span><span class="p">&gt;</span>© 2020.<span class="p">&lt;/</span><span class="nt">i</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">footer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="add-script-in-indexhtml">Add Script in index.html</h2>
<p>Initiate vue and use a couple of routes using router. Include axios from CDN.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/axios/dist/axios.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">component</span><span class="p">(</span><span class="s2">&#34;Navigation&#34;</span><span class="p">,</span> <span class="nx">Navigation</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">Access</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/content&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">Content</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VueRouter</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">routes</span><span class="o">:</span> <span class="nx">routes</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;history&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">router</span><span class="o">:</span> <span class="nx">router</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">authstatus</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">apiurls</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">authget</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/auth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">authpost</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/auth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">postget</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/posts&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setAuthStatus</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Setting authstatus through event to: &#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">authstatus</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New value: &#34;</span><span class="p">,</span> <span class="nx">app</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>As you can see, we created a couple of variables and a method. These will be used by more than one components that we will create next.</p>
<p>Also note that we used <code>mode: &quot;history&quot;</code>. This will necessitate using a server to serve the <code>index.html</code> file. If you don&rsquo;t want that, use <code>mode: &quot;hash&quot;</code>.</p>
<h2 id="create-components">Create Components</h2>
<p>We will not quite use <code>.vue</code> files - although it can be done by using <code>vue-http-loader</code> package when using Vue UMD build. Instead, we will create &ldquo;pseudo-components&rdquo; by just defining the entire Vue template structure in an object.</p>
<p>Let us create three components -</p>
<ol>
<li>Get access details and authenticate</li>
<li>Show content</li>
<li>Navigation: this can be included in the main HTML page or in both of the above components.</li>
</ol>
<h3 id="navigation">Navigation</h3>
<p>Create a new file and call it <code>Navigation.vue.js</code>.</p>
<ol>
<li>Create the Vue template as an object (use Bootstrap for styling)
<ul>
<li>Show access and content links</li>
<li>Display <code>Content</code> links only if user has &ldquo;logged in&rdquo;</li>
</ul>
</li>
<li><code>data</code> (and <code>computed</code>, <code>methods</code> etc.) will be objects as well, and used where relevant (almost represents real Vue single file components)</li>
</ol>
<p>The value to check whether user has logged in comes from the root.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">;</span>
</span></span></code></pre></div><p>Although I am not a fan of accessing variables like this, it serves its purpose in a simple setup. Use Vuex or pass values through props/events if it becomes more complex.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Navigation</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">template</span><span class="o">:</span> <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;nav class=&#34;navbar navbar-expand-md&#34; style=&#34;background-color: #ffffff;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;div class=&#34;nav-brand font-weight-bold&#34;&gt;Vue-Axios Demo&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;button
</span></span></span><span class="line"><span class="cl"><span class="sb">          class=&#34;navbar-toggler&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">          type=&#34;button&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">          data-toggle=&#34;collapse&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">          data-target=&#34;#navbarSupportedContent&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;span class=&#34;navbar-toggler-icon&#34;&gt;&lt;/span&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;/button&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;div class=&#34;collapse navbar-collapse&#34; id=&#34;navbarSupportedContent&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;ul class=&#34;navbar-nav ml-auto mt-2 mt-lg-0 font-weight-bold&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">            &lt;li&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;router-link class=&#34;nav-link&#34; to=&#34;/&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">                Access
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;/router-link&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">            &lt;/li&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">            &lt;li v-if=&#34;authstatus&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;router-link class=&#34;nav-link&#34; to=&#34;content&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">                    Content
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;/router-link&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">            &lt;/li&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">           
</span></span></span><span class="line"><span class="cl"><span class="sb">           
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;/ul&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/nav&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">  `</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">authstatus</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">//  used to show/hide `content` link
</span></span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="access">Access</h3>
<p>Create a new file <code>access.vue.js</code>.</p>
<ol>
<li>Collect email</li>
<li>Check whether email has access through an Axios call to our dummy service</li>
<li>Disable form on successful authentication</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Access</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">template</span><span class="o">:</span> <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;Navigation/&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;h1&gt;Access&lt;/h1&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;div class=&#34;error&#34; v-if=&#34;validationErrors.length &gt; 0&#34; style = &#34;color:red; margin-top: 2em; margin-bottom: 1em;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">            {{ validationErrors }}
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;div v-else style=&#34;margin-top: 2em; margin-bottom: 1em;&#34;&gt;&amp;nbsp;&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;form @submit.prevent=&#34;validateAndSubmit&#34; id=&#34;formLogin&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;div class=&#34;field section&#34; style=&#34;margin-top:3em&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;label for=&#34;email&#34; &gt;Email:&lt;/label&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;input type=&#34;text&#34;  name=&#34;email&#34; v-model=&#34;email&#34; @input=&#34;validate&#34; :disabled=&#34;authstatus&#34;/&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;button style=&#34;margin-left:1em&#34; type=&#34;submit&#34; :disabled=&#34;authstatus&#34;&gt;Access&lt;/button&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;div class=&#34;section&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;i v-if=&#34;authstatus&#34;&gt;You have access! &lt;/i&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">              &lt;i v-else&gt;Verify email to gain access.&lt;/i&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;/form&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;/div&gt;`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">email</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">validationErrors</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">authstatus</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// this way of sharing values is ok for small projects.
</span></span></span><span class="line"><span class="cl">      <span class="c1">// large projects must use Vuex
</span></span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">urls</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// fetch URLs from root
</span></span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">apiurls</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">fetchAuthStatus</span><span class="p">();</span> <span class="c1">// check auth on mount
</span></span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">fetchAuthStatus</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// triggered at load. Relevant only when you have a session.
</span></span></span><span class="line"><span class="cl">      <span class="c1">// for JWT and similar: check against token, and pass refresh token if expired
</span></span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Fetching auth status..&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">urls</span><span class="p">.</span><span class="nx">authget</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">crossdomain</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">authData</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`GET auth done!`</span><span class="p">,</span> <span class="nx">authData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">authData</span><span class="p">[</span><span class="s2">&#34;auth-status&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Auth approved!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// this is silent error since user will ..
</span></span></span><span class="line"><span class="cl">        <span class="c1">// .. most likely not have access the first time the screen loads
</span></span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Auth denied..! Re-login.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;auth-verified&#34;</span><span class="p">,</span> <span class="nx">authData</span><span class="p">[</span><span class="s2">&#34;auth-status&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">login</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// main login flow
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">urls</span><span class="p">.</span><span class="nx">authpost</span><span class="p">,</span> <span class="p">{</span> <span class="nx">email</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">email</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">authData</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`POST auth done!`</span><span class="p">,</span> <span class="nx">authData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">authData</span><span class="p">[</span><span class="s2">&#34;auth-status&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Could not verify email. Validate and resubmit.&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;auth-verified&#34;</span><span class="p">,</span> <span class="nx">authData</span><span class="p">[</span><span class="s2">&#34;auth-status&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span><span class="p">.</span><span class="nx">push</span><span class="p">[</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">validateAndSubmit</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// .. validate before sending the request off to server.
</span></span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">validate</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">login</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">validate</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">errors</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">pattern</span> <span class="o">=</span> <span class="sr">/^(([^&lt;&gt;()[\]\\.,;:\s@&#34;]+(\.[^&lt;&gt;()[\]\\.,;:\s@&#34;]+)*)|(&#34;.+&#34;))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pattern</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">email</span><span class="p">))</span> <span class="nx">errors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">&#34;Invalid email.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">errors</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span> <span class="o">=</span> <span class="nx">errors</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="content">Content</h3>
<p>Create a new file <code>content.vue.js</code>.</p>
<p>Include -</p>
<ol>
<li>template, data and methods</li>
<li>logic to call an external service to get posts</li>
</ol>
<p>Like earlier, we will fetch variables from the root by using <code>this.$root</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Content</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">template</span><span class="o">:</span> <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;Navigation/&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;h1&gt;Content&lt;/h1&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div class=&#34;error&#34; v-if=&#34;validationErrors.length &gt; 0&#34; style = &#34;color:red; margin-top: 2em; margin-bottom: 1em;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        {{ validationErrors }}
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div v-else style=&#34;margin-top: 2em; margin-bottom: 1em;&#34;&gt;&amp;nbsp;&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div class=&#34;field section&#34; style=&#34;margin-top:3em&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;label for=&#34;keywords&#34; &gt;Keywords:&lt;/label&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;input type=&#34;text&#34;  name=&#34;keywords&#34; v-model=&#34;keywords&#34;/&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          &lt;button style=&#34;margin-left:1em&#34; @click=&#34;&#34;&gt;Search&lt;/button&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div class=&#34;field section&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;label for=&#34;posts&#34;&gt;&lt;b&gt;Posts&lt;/b&gt;&lt;/label&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div class=&#34;posts&#34; style=&#34;margin-left:30%; text-align: left!important&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;div v-for=&#34;(post,index) in posts&#34; :key=&#34;index&#34; &gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">          {{post.id}}. {{post.description}}
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        &lt;i v-if=&#34;!posts || posts.length == 0&#34;&gt;No content found.&lt;/i&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;div class=&#34;section&#34; style=&#34;margin-top:1em&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">        
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;/div&gt;`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">keywords</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">posts</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">validationErrors</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">currentPage</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">totalPages</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">authstatus</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// large projects must used Vuex. This will work for small components and data sets.
</span></span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">urls</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// a simple way to standardize URLs from root
</span></span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">$root</span><span class="p">.</span><span class="nx">apiurls</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">fetchPosts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">fetchPosts</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// authstatus check here is not quite required, but is just a fall-back
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;Not logged in. You have to login to fetch posts.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Fetching posts..&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// keywords passed as query - as-is.
</span></span></span><span class="line"><span class="cl">        <span class="c1">// verify whether server requires any specific format
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="sb">`</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">urls</span><span class="p">.</span><span class="nx">postget</span><span class="si">}</span><span class="sb">?query=</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">keywords</span><span class="si">}</span><span class="sb">&amp;page=</span><span class="si">${</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">currentPage</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">          <span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">crossdomain</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">postData</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="sb">`GET posts done!`</span><span class="p">,</span> <span class="nx">postData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;auth-status&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// auth-status is returned by post requests too!
</span></span></span><span class="line"><span class="cl">          <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Auth denied..! Re-login.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s2">&#34;Access is denied.&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">posts</span> <span class="o">=</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;posts&#34;</span><span class="p">]</span> <span class="o">?</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;posts&#34;</span><span class="p">]</span> <span class="o">:</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">currentPage</span> <span class="o">=</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;currentpage&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">          <span class="o">?</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;currentpage&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">          <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">totalPages</span> <span class="o">=</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;totalpages&#34;</span><span class="p">]</span> <span class="o">?</span> <span class="nx">postData</span><span class="p">[</span><span class="s2">&#34;totalpages&#34;</span><span class="p">]</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// pagination logic yet to be implemented to navigate to subsequent pages
</span></span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;.. posts fetched!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">validateAndSubmit</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//  there are no validations at this time.
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">validationErrors</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">fetchPosts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="indexhtml">Index.html</h3>
<p>We will change index.html to include -</p>
<ol>
<li>the newly developed components</li>
<li>common variables used across components</li>
</ol>
<p>Change the <code>script</code> section with following code -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./navigation.vue.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./access.vue.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;./content.vue.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://unpkg.com/axios/dist/axios.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">component</span><span class="p">(</span><span class="s2">&#34;Navigation&#34;</span><span class="p">,</span> <span class="nx">Navigation</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">component</span><span class="p">(</span><span class="s2">&#34;Content&#34;</span><span class="p">,</span> <span class="nx">Content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">component</span><span class="p">(</span><span class="s2">&#34;Access&#34;</span><span class="p">,</span> <span class="nx">Access</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">Access</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/content&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">Content</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VueRouter</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">routes</span><span class="o">:</span> <span class="nx">routes</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;history&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">base</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">el</span><span class="o">:</span> <span class="s2">&#34;#app&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">router</span><span class="o">:</span> <span class="nx">router</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">authstatus</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">apiurls</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">authget</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/auth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">authpost</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/auth&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">postget</span><span class="o">:</span> <span class="s2">&#34;http://localhost:9000/posts&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setAuthStatus</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Setting authstatus through event to: &#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">authstatus</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New value: &#34;</span><span class="p">,</span> <span class="nx">app</span><span class="p">.</span><span class="nx">authstatus</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And ta da.. your application is ready. Just serve the directory that has <code>index.html</code>. For e.g. we can use a quick http server using <code>http-server</code>:</p>
<pre tabindex="0"><code>npm i -g http-server
http-server
</code></pre><p>If you use <code>mode: &quot;hash&quot;</code>, you can simply open <code>index.html</code> to see the magic.</p>
<h2 id="how-it-works">How it works?</h2>
<ul>
<li>Parent HTML page instantiates Vue and mounts in on a <code>div</code>.</li>
<li>Three JS files that define Vue templates, methods, et al. as objects.</li>
<li>Component objects defined in the external JS files are imported and used in parent HTML page.</li>
</ul>
<p>Not quite beautiful or modular, but it works!</p>
<p>See this code on <a href="https://github.com/techformist/simple-vue-html-axios-demo">Github</a></p>
<h2 id="conclusion">Conclusion</h2>
<p>Using Vue from CDN can certainly play a role in super simple projects when there are people in the ecosystem who are not quite onboard with Vue, and want to understand how things work by seeing a simple project.</p>
<p>But, I find it cleaner to just use Vue CLI and its single file components. While Vue CLI does bring in complexity in build processes, the overall structure is easier to understand and demonstrate to a technical audience.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Using Vue Plugins in Quasar</title>
      <link>https://techformist.com/using-vue-plugins-quasar/</link>
      <pubDate>Fri, 22 May 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/using-vue-plugins-quasar/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a quick way to use Vue plugins in Quasar.&lt;/p&gt;
&lt;h2 id=&#34;use-case-frappe-charts-in-vue&#34;&gt;Use Case: Frappe Charts in Vue&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s consider a simple use case for using Vue plugins - we want to use Frappe charts.&lt;/p&gt;
&lt;p&gt;We can simply use &lt;a href=&#34;https://github.com/JustSteveKing/vue2-frappe&#34;&gt;vue2-frappe&lt;/a&gt; to easily do that. Just install the package in your project -&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmd&#34; data-lang=&#34;cmd&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm i --save vue2-frappe
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, register it as a Vue plugin -&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Vue&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;vue&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Chart&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;vue2-frappe&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Vue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Chart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;frappe-in-quasar&#34;&gt;Frappe in Quasar&lt;/h2&gt;
&lt;p&gt;In Quasar you can&amp;rsquo;t do a &lt;code&gt;Vue.use&lt;/code&gt; like the above. There is no &lt;code&gt;main.js&lt;/code&gt; to orchestrate that.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a quick way to use Vue plugins in Quasar.</p>
<h2 id="use-case-frappe-charts-in-vue">Use Case: Frappe Charts in Vue</h2>
<p>Let&rsquo;s consider a simple use case for using Vue plugins - we want to use Frappe charts.</p>
<p>We can simply use <a href="https://github.com/JustSteveKing/vue2-frappe">vue2-frappe</a> to easily do that. Just install the package in your project -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">npm i --save vue2-frappe
</span></span></code></pre></div><p>Next, register it as a Vue plugin -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Chart</span> <span class="nx">from</span> <span class="s2">&#34;vue2-frappe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Chart</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="frappe-in-quasar">Frappe in Quasar</h2>
<p>In Quasar you can&rsquo;t do a <code>Vue.use</code> like the above. There is no <code>main.js</code> to orchestrate that.</p>
<p>The documentation of <code>vue2-frappe</code> does say that we can use <code>vue2-frappe</code> directly in components..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">VueFrappe</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue2-frappe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">VueFrappe</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>.. but, I was never successful in getting that to work.</p>
<p>Time is always of essence - so I did the next best thing. I used a <a href="/create-boot-plugin-quasar">boot plugin</a> to register Vue plugins.</p>
<p>Create <code>./src/boot/vplugins.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Chart</span> <span class="nx">from</span> <span class="s2">&#34;vue2-frappe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Similar to Vue.use()
</span></span></span><span class="line"><span class="cl"><span class="nx">Chart</span><span class="p">.</span><span class="nx">install</span><span class="p">(</span><span class="nx">Vue</span><span class="p">);</span>
</span></span></code></pre></div><p>Register this new plugin in <code>quasar.conf</code>..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="cm">/* ctx */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// app boot file (/src/boot)
</span></span></span><span class="line"><span class="cl">    <span class="c1">// --&gt; boot files are part of &#34;main.js&#34;
</span></span></span><span class="line"><span class="cl">    <span class="c1">// https://quasar.dev/quasar-cli/cli-documentation/boot-files
</span></span></span><span class="line"><span class="cl">    <span class="nx">boot</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;vplugins&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>You can now happily use frappe charts from any component -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">vue-frappe</span>
</span></span><span class="line"><span class="cl">      <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;trend[&#39;labels&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">id</span><span class="o">=</span><span class="s">&#34;prodTrend&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:labels</span><span class="o">=</span><span class="s">&#34;trend[&#39;labels&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:dataSets</span><span class="o">=</span><span class="s">&#34;trend[&#39;datasets&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">type</span><span class="o">=</span><span class="s">&#34;bar&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:height</span><span class="o">=</span><span class="s">&#34;300&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">:colors</span><span class="o">=</span><span class="s">&#34;[&#39;blue&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">vue-frappe</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">trend</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// provide options for Frappe chart
</span></span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">&gt;
</span></span></code></pre></div><p>Have additional plugins to register? Instead of creating distinct files for more than one Vue plugin, we just combine everything in one file - just change <code>vplugins.js</code> and you are all set.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Chart</span> <span class="nx">from</span> <span class="s2">&#34;vue2-frappe&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">GobblyGook</span> <span class="nx">from</span> <span class="s2">&#34;gobbly&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Chart</span><span class="p">.</span><span class="nx">install</span><span class="p">(</span><span class="nx">Vue</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">GobblyGook</span><span class="p">.</span><span class="nx">install</span><span class="p">(</span><span class="nx">Vue</span><span class="p">);</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>Create Boot Plugin in Quasar</title>
      <link>https://techformist.com/create-boot-plugin-quasar/</link>
      <pubDate>Wed, 20 May 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/create-boot-plugin-quasar/</guid>
      <description>&lt;p&gt;Quasar CLI structures project differently as compared to a standard Vue CLI app. As a result, you cannot just follow examples on the Internets blindly and use &lt;code&gt;Vue.use()&lt;/code&gt;, or start changing code in &lt;code&gt;main.js&lt;/code&gt;. There are, however, great ways of addressing the same problems with slightly different solutions.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&#34;https://quasar.dev/quasar-cli/cli-documentation/boot-files&#34;&gt;boot files&lt;/a&gt;. These were simply called app plugins which led to them being mixed up with Quasar&amp;rsquo;s own plugins. Now they are simply referred to as &amp;ldquo;boot files&amp;rdquo;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Quasar CLI structures project differently as compared to a standard Vue CLI app. As a result, you cannot just follow examples on the Internets blindly and use <code>Vue.use()</code>, or start changing code in <code>main.js</code>. There are, however, great ways of addressing the same problems with slightly different solutions.</p>
<p>Enter <a href="https://quasar.dev/quasar-cli/cli-documentation/boot-files">boot files</a>. These were simply called app plugins which led to them being mixed up with Quasar&rsquo;s own plugins. Now they are simply referred to as &ldquo;boot files&rdquo;.</p>
<h2 id="why-boot-files">Why Boot Files?</h2>
<p>Use cases for boot files include -</p>
<ol>
<li>Use common start-up code for your app</li>
<li>Enable functions that are accessible from anywhere</li>
<li>Initiliase your own dependencies</li>
</ol>
<p>A great example is provided out of the box in axios. If you select <code>axios</code> library to be enabled while creating project using Quasar CLI, you will find a boot file in <code>./src/boot/axios.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">axios</span> <span class="nx">from</span> <span class="s2">&#34;axios&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$axios</span> <span class="o">=</span> <span class="nx">axios</span><span class="p">;</span>
</span></span></code></pre></div><p>Next, this plugin is registered in <code>quasar.conf.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="cm">/* ctx */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">boot</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;axios&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>In any component, you can now do -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$axios</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span>
</span></span></code></pre></div><p>.. without the need to import axios.</p>
<h2 id="create-a-simple-boot-plugin">Create a simple boot plugin</h2>
<p>The axios plugin works just fine as-is. But, you have no way to provide options in one common place. Take an example of providing just the bearer token -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">res</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="sb">`store.state.baseURL/</span><span class="si">${</span><span class="nx">reqURL</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">reqData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">Authorization</span><span class="o">:</span> <span class="nx">store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">bearerToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>Clutter affects code readability and maintenance - more importantly, we lose &ldquo;cool&rdquo; factor.</p>
<p>Let&rsquo;s create our own version of Axios so that bearer token is included automatically for authentication.</p>
<p>If you are curious - we could always extend the current plugin, but I found it the hard way that it led to confusion for people on the beginner side of spectrum (&ldquo;axios automatically authenticates in that project, but there is something wrong here&rdquo;).</p>
<p>Create a new file <code>./src/boot/req.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">axios</span> <span class="nx">from</span> <span class="s2">&#34;axios&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">req</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">async</span> <span class="nx">post</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="kr">await</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="sb">`https://baseURL.com/</span><span class="si">${</span><span class="nx">url</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">Authorization</span><span class="o">:</span> <span class="s2">&#34;token&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">Vue</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// something to do
</span></span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Vue</span> <span class="o">=</span> <span class="nx">Vue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$req</span> <span class="o">=</span> <span class="nx">req</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>We are doing simple things here -</p>
<ol>
<li>Create an object with a function</li>
<li>Create a function within the object that makes axios call with arguments passed to the function</li>
<li>Register object to be globally available through a Vue prototype</li>
</ol>
<p>Register the plugin in <code>quasar.conf.js</code>..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="cm">/* ctx */</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">boot</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;req&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>You can test this code from any component..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!-- Stuff goes here --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">async</span> <span class="nx">updateRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">let</span> <span class="nx">reqData</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">a</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">b</span><span class="o">:</span> <span class="mi">2</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="p">{</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">$req</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">&#34;get-sum&#34;</span><span class="p">,</span> <span class="nx">reqData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;data&#34;</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="accessing-the-store">Accessing the Store</h2>
<p>Wait, there&rsquo;s more.</p>
<p>There is no need to hard-code values when you can bring them from one central place - from the Vuex store.</p>
<p>Let&rsquo;s do that.</p>
<p>Modify <code>./src/boot/req.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">axios</span> <span class="nx">from</span> <span class="s2">&#34;axios&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">Vue</span><span class="p">,</span> <span class="nx">store</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// something to do
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">req</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">post</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kr">await</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="k">this</span><span class="p">.</span><span class="nx">store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">baseURL</span><span class="si">}</span><span class="sb">/</span><span class="si">${</span><span class="nx">url</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="nx">data</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">Authorization</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">bearerToken</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// throw if you want to pass errors back
</span></span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">req</span><span class="p">.</span><span class="nx">Vue</span> <span class="o">=</span> <span class="nx">Vue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$req</span> <span class="o">=</span> <span class="nx">req</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>We have included <code>store</code> argument and moved <code>req</code> within the exported function (which was not strictly required, but it avoids creating more variables and confusion with <code>this</code>).</p>
<p>You can now use <code>this.$req.post()</code> from anywhere within your app, and authentication is automatically taken care for you.</p>
<p>Similar to <code>Vue</code> and <code>store</code>, there are other arguments available to your boot function -</p>
<ul>
<li>router</li>
<li>ssrContext: useful in server rendered contexts</li>
<li>app: object that root component instantiates with</li>
</ul>
<p>Read more in <a href="https://quasar.dev/quasar-cli/cli-documentation/boot-files">docs</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Access Vuex Store in a Quasar App</title>
      <link>https://techformist.com/access-vuex-store-quasar/</link>
      <pubDate>Wed, 06 May 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/access-vuex-store-quasar/</guid>
      <description>&lt;p&gt;Quasar is great. But, it is also a bit different when it comes to accessing the Vuex store from your code.&lt;/p&gt;
&lt;h2 id=&#34;why-is-quasar-any-different&#34;&gt;Why is Quasar any different?&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Is it not just Vue?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Well, yes. But.. the project structure created by Quasar CLI differs from a &amp;ldquo;normal&amp;rdquo; that by Vue CLI. Quasar CLI builds on top of Vue as well but may not follow similar practices to other frameworks / libraries using Vue.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Quasar is great. But, it is also a bit different when it comes to accessing the Vuex store from your code.</p>
<h2 id="why-is-quasar-any-different">Why is Quasar any different?</h2>
<p>&ldquo;Is it not just Vue?&rdquo;</p>
<p>Well, yes. But.. the project structure created by Quasar CLI differs from a &ldquo;normal&rdquo; that by Vue CLI. Quasar CLI builds on top of Vue as well but may not follow similar practices to other frameworks / libraries using Vue.</p>
<p>If you can&rsquo;t really move away from the standard structure, just use Quasar as a plugin in a project created by Vue CLI. You may <a href="https://quasar.dev/start/pick-quasar-flavour">lose some features</a>, but you may find yourself at home and stay happy. In that case, you could leverage your existing knowledge on using <a href="/access-update-vuex-store">Vuex states and mutations in multiple ways</a>.</p>
<p>Or.. you could just experiment more and accept a different normal.</p>
<p>An app created by Quasar CLI does not have <code>main.js</code>. Vue compile/load sequence is taken care by quasar with options defined in a <code>/quasar.conf</code> file.</p>
<p>The <code>store/index.js</code> file in Vue CLI will have a function invocation -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>This store is then used in <code>main.js</code>..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s2">&#34;./store&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">router</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">store</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">render</span><span class="o">:</span> <span class="p">(</span><span class="nx">h</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">App</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="nx">$mount</span><span class="p">(</span><span class="s2">&#34;#app&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>This and the corresponding setup within Vue CLI projects make it convenient to access states or mutations (in addition to helper functions).</p>
<ol>
<li>
<p>Use <code>this.$store</code> in components, store modules, or router</p>
</li>
<li>
<p>Directly import <code>store</code> and use it to access store functions</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s2">&#34;./store&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">isLoggedIn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// how do you penalise a person without login
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
</ol>
<p>In Quasar CLI, the <code>/store/index.js</code> file returns a <code>store</code> object..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span><span class="p">({</span> <span class="nx">ssrContext</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">Store</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">namespaced</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1">//...
</span></span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">Store</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>&hellip; but this is not used in the same way as Vue CLI.</p>
<p>Also, the modules are structured within folders in <code>/src/store</code> by default. And.. you can use <a href="https://quasar.dev/quasar-cli/cli-documentation/vuex-store">quasar CLI to create a new module</a>.</p>
<p>Directly accessing store will not work -</p>
<ol>
<li>
<p>Can&rsquo;t just import store</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s2">&#34;./store&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">store</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="s2">&#34;blah&#34;</span><span class="p">,</span> <span class="nx">val</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// store is a function and does not support `set`
</span></span></span></code></pre></div></li>
<li>
<p><code>this.$store</code> is <code>undefined</code> and not available in stores / modules</p>
</li>
</ol>
<h2 id="how-can-you-use-a-vuex-store-in-quasar">How can you use a Vuex store in Quasar?</h2>
<p>You have more than one way to get to the store..!</p>
<h5 id="use-helper-functions">Use helper functions</h5>
<p>Continue to use <code>mapState</code>, <code>mapMutations</code>, and friends to manage your state.</p>
<h5 id="use-pathify">Use Pathify</h5>
<p>Works great everywhere. Use <code>sync</code> in store modules or in components. See working with <a href="/avoid-vuex-boilerplate-pathify/">Pathify</a>.</p>
<h5 id="use-normal-commit">Use &ldquo;normal&rdquo; <code>commit</code></h5>
<p>Fall back on the trusty commit in store modules!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;contact&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">namespaced</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="nx">state</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">actions</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">async</span> <span class="nx">updateContact</span><span class="p">({</span> <span class="nx">commit</span> <span class="p">},</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">//...
</span></span></span><span class="line"><span class="cl">      <span class="nx">commit</span><span class="p">(</span><span class="s2">&#34;first_name&#34;</span><span class="p">,</span> <span class="s2">&#34;Ram&#34;</span><span class="p">);</span> <span class="c1">// within this module
</span></span></span><span class="line"><span class="cl">      <span class="nx">commit</span><span class="p">(</span><span class="s2">&#34;action/updateCall&#34;</span><span class="p">,</span> <span class="nx">actionData</span><span class="p">,</span> <span class="p">{</span> <span class="nx">root</span><span class="o">:</span> <span class="kc">true</span> <span class="p">});</span> <span class="c1">// mutation from `action`
</span></span></span><span class="line"><span class="cl">      <span class="c1">//...
</span></span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h5 id="change-the-default-store-structure">Change the default store structure</h5>
<p>Just change the Vuex store structure to access it from anywhere.</p>
<p>Modify <code>./src/store/index.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vuex</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Use a new variable and export values to change default behaviour.
</span></span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">store</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// .. other code
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">Store</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">namespaced</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;global&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">state</span><span class="o">:</span> <span class="p">{</span> <span class="nx">answer</span><span class="o">:</span> <span class="mi">42</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// mutations / getters / plugins/ other code
</span></span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// add this so that we export store
</span></span></span><span class="line"><span class="cl">  <span class="nx">store</span> <span class="o">=</span> <span class="nx">Store</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Quasar default
</span></span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">Store</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// add this line to access store wherever you need
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="nx">store</span> <span class="p">};</span>
</span></span></code></pre></div><p>This is useful to access state as well as commits.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// ./src/router.js
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s2">&#34;./store/index&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h5 id="use-boot-time-plugins-app-plugins-in-quasar">Use <code>boot</code> time plugins (&ldquo;app plugins&rdquo;) in Quasar</h5>
<p>Create a new plugin (say <code>superfunc</code>) by creating a new file in <code>/src/boot/superfunc.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">superfunc</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="nx">store</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">store</span><span class="p">,</span> <span class="nx">Vue</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// something to do
</span></span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">superfunc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">superfunc</span><span class="p">.</span><span class="nx">store</span> <span class="o">=</span> <span class="nx">store</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$superfunc</span> <span class="o">=</span> <span class="nx">superfunc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Then happily use <code>superfunc</code> in places -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;story store&#34;</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">_vm</span><span class="p">.</span><span class="nx">$superfunc</span><span class="p">.</span><span class="nx">store</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="parting-words">Parting Words</h2>
<p>Not quite what you call &ldquo;super fun&rdquo;, but stores simply work in Quasar, and that&rsquo;s all there is to it.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Learn FeathersJS by Building a Simple CRM - Client App</title>
      <link>https://techformist.com/learn-feathersjs-realworld-client-app/</link>
      <pubDate>Wed, 22 Apr 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/learn-feathersjs-realworld-client-app/</guid>
      <description>&lt;p&gt;Here we see how to build a real-world client application for your FeathersJS backend. We will use Vue + Feathers-Vuex and quickly create the frontend app.&lt;/p&gt;
&lt;h2 id=&#34;building-the-client-application&#34;&gt;Building the Client Application&lt;/h2&gt;
&lt;p&gt;Previously you saw how we could &lt;a href=&#34;https://techformist.com/learn-feathersjs-build-app/&#34;&gt;use Feathers to quickly build a backend application&lt;/a&gt;. The beauty of feathers is not only that it is quick to build, but it is also universal.&lt;/p&gt;
&lt;p&gt;We can use FeathersJS and Feathers-Vuex (our choice of client storage) to conjure up a client application that totally blows away our users.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here we see how to build a real-world client application for your FeathersJS backend. We will use Vue + Feathers-Vuex and quickly create the frontend app.</p>
<h2 id="building-the-client-application">Building the Client Application</h2>
<p>Previously you saw how we could <a href="/learn-feathersjs-build-app/">use Feathers to quickly build a backend application</a>. The beauty of feathers is not only that it is quick to build, but it is also universal.</p>
<p>We can use FeathersJS and Feathers-Vuex (our choice of client storage) to conjure up a client application that totally blows away our users.</p>
<p>Our choice of technology for building client -</p>
<ol>
<li>Vue + Feathers-Vuex</li>
<li>Vuetify</li>
</ol>
<h2 id="client-get-started">Client: Get Started</h2>
<p>This is what we will build for the client..</p>
<p><img loading="lazy" src="/2020/feathersjs-vuex-sample-client-app.gif" type="" alt="feathersjs-vuex-sample-client-app"  /></p>
<p>Let&rsquo;s get started.</p>
<blockquote>
<p>All great things in life starts with Vue CLI.</p>
</blockquote>
<p>But, we don&rsquo;t have the time and this is not a Vue tutorial. So, it&rsquo;s time to work the magic and improve that statement.</p>
<p>So, we change the above quote..</p>
<blockquote>
<p>A few great things start with boilerplates.</p>
</blockquote>
<p>Unfortunately, again, we don&rsquo;t quite have a starter-friendly boilerplate. We can either start from our <a href="https://github.com/techformist/vue-vuetify-api-boost.git">vue-vuetify-booster</a> boilerplate, or start afresh.</p>
<p>Either ways your Vue application should be setup in the <code>client</code> folder that is adjacent to the <code>server</code> folder where FeathersJS server resides. We will get our design inspiration from the above boilerplate.</p>
<ul>
<li>Panel / Subpanel etc. are layouts defined in that boilerplate - we will reuse them</li>
<li>There are a few useful ways shortcuts to show snackbar, alerts etc, which we carry forward to this project</li>
<li>The home page, toolbar and navigation drawers will remain largely unchanged except for the links</li>
</ul>
<p>See the <a href="https://github.com/techformist/featherlight-crm">Github repository for Featherlight CRM</a> when in doubt.</p>
<p>At a high level, the client <code>package.json</code> has -</p>
<ol>
<li>Vue + Vuetify</li>
<li>FeathersJS recommended packages. We will use communication over socket.io (alternatively we can use REST, but real-time using socket is more exciting)
<pre tabindex="0"><code>npm i @feathersjs/feathers @feathersjs/rest-client @feathersjs/authentication-client @vue/composition-api feathers-vuex feathers-hooks-common --save
</code></pre></li>
</ol>
<p>Add/modify <code>transpileDependencies</code> line to your <code>vue.config.js</code> -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// code
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">transpileDependencies</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;vuetify&#34;</span><span class="p">,</span> <span class="s2">&#34;feathers-vuex&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// more code
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Add a new file <code>feathers-client.js</code> to implement feathers client -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">feathers</span> <span class="nx">from</span> <span class="s2">&#34;@feathersjs/feathers&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">auth</span> <span class="nx">from</span> <span class="s2">&#34;@feathersjs/authentication-client&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">feathersVuex</span> <span class="nx">from</span> <span class="s2">&#34;feathers-vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">socketio</span> <span class="nx">from</span> <span class="s2">&#34;@feathersjs/socketio-client&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">io</span> <span class="nx">from</span> <span class="s2">&#34;socket.io-client&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">iff</span><span class="p">,</span> <span class="nx">discard</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;feathers-hooks-common&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">socket</span> <span class="o">=</span> <span class="nx">io</span><span class="p">(</span><span class="s2">&#34;http://localhost:3030&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">transports</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;websocket&#34;</span><span class="p">]</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">feathersClient</span> <span class="o">=</span> <span class="nx">feathers</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">configure</span><span class="p">(</span><span class="nx">socketio</span><span class="p">(</span><span class="nx">socket</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">configure</span><span class="p">(</span><span class="nx">auth</span><span class="p">({</span> <span class="nx">storage</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span> <span class="p">}))</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">hooks</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">before</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">all</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="nx">iff</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">[</span><span class="s2">&#34;create&#34;</span><span class="p">,</span> <span class="s2">&#34;update&#34;</span><span class="p">,</span> <span class="s2">&#34;patch&#34;</span><span class="p">].</span><span class="nx">includes</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">method</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="nx">discard</span><span class="p">(</span><span class="s2">&#34;__id&#34;</span><span class="p">,</span> <span class="s2">&#34;__isTemp&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">feathersClient</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Setting up feathers-vuex
</span></span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">makeServicePlugin</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">makeAuthPlugin</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">BaseModel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">models</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">FeathersVuex</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="o">=</span> <span class="nx">feathersVuex</span><span class="p">(</span><span class="nx">feathersClient</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">serverAlias</span><span class="o">:</span> <span class="s2">&#34;api&#34;</span><span class="p">,</span> <span class="c1">// optional for working with multiple APIs (this is the default value)
</span></span></span><span class="line"><span class="cl">  <span class="nx">idField</span><span class="o">:</span> <span class="s2">&#34;id&#34;</span><span class="p">,</span> <span class="c1">// Must match the id field in your database table/collection
</span></span></span><span class="line"><span class="cl">  <span class="nx">whitelist</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;$regex&#34;</span><span class="p">,</span> <span class="s2">&#34;$options&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="nx">makeAuthPlugin</span><span class="p">,</span> <span class="nx">makeServicePlugin</span><span class="p">,</span> <span class="nx">BaseModel</span><span class="p">,</span> <span class="nx">models</span><span class="p">,</span> <span class="nx">FeathersVuex</span> <span class="p">};</span>
</span></span></code></pre></div><p>You should now be able to run the application without issues..</p>
<pre tabindex="0"><code>npm run serve
</code></pre><h2 id="router">Router</h2>
<p>Not a lot of interesting things here.. We just define a dynamic home to redirect users to dashboard or to the generic home page. Everything else is pretty standard.</p>
<p>Note that we have not implemented any navigation guards - but that is easy enough to do by uncommenting the <code>requireAuth</code> function and using that for the routes that need prior authentication.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s2">&#34;../store/index&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">VueRouter</span> <span class="nx">from</span> <span class="s2">&#34;vue-router&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Home</span> <span class="nx">from</span> <span class="s2">&#34;../views/Home.vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">VueRouter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// function requireAuth(to, from, next) {
</span></span></span><span class="line"><span class="cl"><span class="c1">//   if (store.state.auth.accessToken) next();
</span></span></span><span class="line"><span class="cl"><span class="c1">//   else next(&#34;/login&#34;);
</span></span></span><span class="line"><span class="cl"><span class="c1">// }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">dynamicHome</span><span class="p">(</span><span class="nx">to</span><span class="p">,</span> <span class="nx">from</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// this should be ideally replaced by store.getters[&#34;auth/isAuthenticated&#34;]
</span></span></span><span class="line"><span class="cl">  <span class="c1">// .. in newer versions
</span></span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">store</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">)</span> <span class="nx">next</span><span class="p">(</span><span class="s2">&#34;/dashboard&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span> <span class="nx">next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">logout</span><span class="p">(</span><span class="nx">to</span><span class="p">,</span> <span class="nx">from</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">(</span><span class="s2">&#34;auth/logout&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">next</span><span class="p">(</span><span class="s2">&#34;/login&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">routes</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;home&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="nx">Home</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">beforeEnter</span><span class="o">:</span> <span class="nx">dynamicHome</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/signup&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;signup&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;register&#34; */</span> <span class="s2">&#34;../views/Register.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/login&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;login&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;login&#34; */</span> <span class="s2">&#34;../views/Login.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/logout&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;logout&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">beforeEnter</span><span class="o">:</span> <span class="nx">logout</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/terms&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Terms&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;terms&#34; */</span> <span class="s2">&#34;../views/Terms.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/dashboard&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Dashboard&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;dashboard&#34; */</span> <span class="s2">&#34;../views/Dashboard.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// beforeEnter: requireAuth
</span></span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/contacts&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Contact&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Contact.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/activities&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Activity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/Activity.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/contact-us&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;Contact Us&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;contact-us&#34; */</span> <span class="s2">&#34;../views/ContactUs.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;/about&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;About&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// route level code-splitting
</span></span></span><span class="line"><span class="cl">    <span class="c1">// this generates a separate chunk (about.[hash].js) for this route
</span></span></span><span class="line"><span class="cl">    <span class="c1">// which is lazy-loaded when the route is visited.
</span></span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;about&#34; */</span> <span class="s2">&#34;../views/About.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// catch all 404
</span></span></span><span class="line"><span class="cl">    <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;../views/NotFound.vue&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VueRouter</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;history&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">base</span><span class="o">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">BASE_URL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">routes</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">router</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="vuex">Vuex</h2>
<p>Add a new folder called <code>store/services</code> and create <code>users.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">feathersClient</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">makeServicePlugin</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">BaseModel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;../../feathers-client&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">User</span> <span class="kr">extends</span> <span class="nx">BaseModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">constructor</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">super</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Required for $FeathersVuex plugin to work after production transpile.
</span></span></span><span class="line"><span class="cl">  <span class="kr">static</span> <span class="nx">modelName</span> <span class="o">=</span> <span class="s2">&#34;User&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Define default properties here
</span></span></span><span class="line"><span class="cl">  <span class="kr">static</span> <span class="nx">instanceDefaults</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">email</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">password</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">servicePath</span> <span class="o">=</span> <span class="s2">&#34;users&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">servicePlugin</span> <span class="o">=</span> <span class="nx">makeServicePlugin</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Model</span><span class="o">:</span> <span class="nx">User</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">service</span><span class="o">:</span> <span class="nx">feathersClient</span><span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="nx">servicePath</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="nx">servicePath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Setup the client-side Feathers hooks.
</span></span></span><span class="line"><span class="cl"><span class="nx">feathersClient</span><span class="p">.</span><span class="nx">service</span><span class="p">(</span><span class="nx">servicePath</span><span class="p">).</span><span class="nx">hooks</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">before</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">all</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">find</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">create</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">update</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">patch</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">remove</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">after</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">all</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">find</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">create</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">update</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">patch</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">remove</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">error</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">all</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">find</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">create</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">update</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">patch</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">remove</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">servicePlugin</span><span class="p">;</span>
</span></span></code></pre></div><p>As you see - this is has the familiar structure of FeathersJS. We can fine tune this and change app behaviour, but will leave it that for now.</p>
<p>Create a similar file for <code>contacts</code> - we will use the name <code>contactsPlugin</code> instead of <code>servicePlugin</code>. Rest remains the same.</p>
<p>We will register these plugins in the store.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Vuex</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">FeathersVuex</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;../feathers-client&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">snackbar</span> <span class="nx">from</span> <span class="s2">&#34;./snackbar&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">pref</span> <span class="nx">from</span> <span class="s2">&#34;./pref&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">alert</span> <span class="nx">from</span> <span class="s2">&#34;./alert&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">auth</span> <span class="nx">from</span> <span class="s2">&#34;./auth&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">servicePlugin</span> <span class="nx">from</span> <span class="s2">&#34;./services/users&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">contactsPlugin</span> <span class="nx">from</span> <span class="s2">&#34;./services/contacts&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">FeathersVuex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">namespaced</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span><span class="o">:</span> <span class="s2">&#34;global&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">snackbar</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pref</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">alert</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">auth</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">servicePlugin</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">contactsPlugin</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">auth</span><span class="p">,</span> <span class="nx">servicePlugin</span><span class="p">,</span> <span class="nx">contactsPlugin</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">state</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">getters</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">isLoggedIn</span><span class="p">(</span><span class="nx">state</span><span class="p">,</span> <span class="nx">getters</span><span class="p">,</span> <span class="nx">rootState</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="o">!!</span><span class="nx">rootState</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>You will see a few modules not used by feathers - it is just to make the application more pleasing. You can ignore them.</p>
<h2 id="views">Views</h2>
<p>Let us create the <code>Contact</code> view. This is simple and straight forward - we just render the list component here.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">Panel</span> <span class="nx">icon</span><span class="o">=</span><span class="s2">&#34;mdi-book-plus&#34;</span> <span class="nx">title</span><span class="o">=</span><span class="s2">&#34;Contacts&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">template</span> <span class="nx">slot</span><span class="o">=</span><span class="s2">&#34;toolbar-items&#34;</span><span class="o">&gt;&lt;</span><span class="err">/template&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">template</span> <span class="nx">slot</span><span class="o">=</span><span class="s2">&#34;content&#34;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">ContactList</span> <span class="o">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/template&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="err">/Panel&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/template&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Panel</span> <span class="nx">from</span> <span class="s2">&#34;../components/layouts/Panel&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">ContactList</span> <span class="nx">from</span> <span class="s2">&#34;../components/ContactList&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Panel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">ContactList</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">/script&gt;</span>
</span></span></code></pre></div><h2 id="contact-view---list-component">Contact View - List Component</h2>
<p>Next, let us create <code>components/ContactList.vue</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">PanelListMain</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">slot</span><span class="o">=</span><span class="s">&#34;toolbar-items&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;subtitle-2&#34;</span><span class="p">&gt;</span>Contact List<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;newRecord&#34;</span> <span class="na">small</span> <span class="na">outlined</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">small</span><span class="p">&gt;</span>mdi-plus<span class="p">&lt;/</span><span class="nt">v-icon</span><span class="p">&gt;</span>New
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">template</span> <span class="na">slot</span><span class="o">=</span><span class="s">&#34;content&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">v-row</span> <span class="na">dense</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">flat</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;transparent&#34;</span> <span class="na">height</span><span class="o">=</span><span class="s">&#34;100%&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;overflow:auto&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-card-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-text-field</span>
</span></span><span class="line"><span class="cl">                <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;srchLastName&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">prepend-icon</span><span class="o">=</span><span class="s">&#34;mdi-magnify&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">label</span><span class="o">=</span><span class="s">&#34;Search Last Name&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">single-line</span>
</span></span><span class="line"><span class="cl">              <span class="p">&gt;&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-card-title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-data-table</span>
</span></span><span class="line"><span class="cl">              <span class="na">:headers</span><span class="o">=</span><span class="s">&#34;headers&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">:items</span><span class="o">=</span><span class="s">&#34;contacts&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="na">hide-default-footer</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot:item</span><span class="o">=</span><span class="s">&#34;props&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.first_name }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.last_name }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.title }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.status_cd }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.email }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>{{ props.item.phone }}<span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-icon</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;success&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;editRecord(props.item)&#34;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&gt;</span>mdi-pencil<span class="p">&lt;/</span><span class="nt">v-icon</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">td</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">tr</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-data-table</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="c">&lt;!-- &lt;v-col cols=&#34;12&#34; class=&#34;text-md-right pt-2&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="c">          &lt;v-pagination
</span></span></span><span class="line"><span class="cl"><span class="c">            v-model=&#34;contacts.page&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">            :total-visible=&#34;7&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">            :length=&#34;contacts.lastPage&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">            @input=&#34;changePage&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">            justify=&#34;end&#34;
</span></span></span><span class="line"><span class="cl"><span class="c">          &gt;&lt;/v-pagination&gt;
</span></span></span><span class="line"><span class="cl"><span class="c">        &lt;/v-col&gt; --&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">ContactEdit</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;detailDialog&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;detailDialog&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">:currentItem</span><span class="o">=</span><span class="s">&#34;currentItem&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">PanelListMain</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">makeFindMixin</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;feathers-vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">PanelListMain</span> <span class="nx">from</span> <span class="s2">&#34;./layouts/PanelListMain&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">detailDialog</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;First Name&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;first_name&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Last Name&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;last_name&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Title&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;title&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Status&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;status_cd&#34;</span><span class="p">,</span> <span class="nx">sortable</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Email&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;email&#34;</span><span class="p">,</span> <span class="nx">sortable</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Phone&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;phone&#34;</span><span class="p">,</span> <span class="nx">sortable</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">          <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="s2">&#34;Actions&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;actions&#34;</span><span class="p">,</span> <span class="nx">sortable</span><span class="o">:</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nx">srchLastName</span><span class="o">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">currentItem</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">contactsParams</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span> <span class="nx">query</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">search</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">search</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">srchStr</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">srchLastName</span><span class="p">)</span> <span class="nx">srchStr</span><span class="p">[</span><span class="s2">&#34;last_name&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">srchLastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nx">srchStr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">mixins</span><span class="o">:</span> <span class="p">[</span><span class="nx">makeFindMixin</span><span class="p">({</span> <span class="nx">service</span><span class="o">:</span> <span class="s2">&#34;contacts&#34;</span> <span class="p">})],</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// changePage(page) {
</span></span></span><span class="line"><span class="cl">      <span class="c1">//   this.fetchContact({
</span></span></span><span class="line"><span class="cl">      <span class="c1">//     page: page,
</span></span></span><span class="line"><span class="cl">      <span class="c1">//     query: this.search,
</span></span></span><span class="line"><span class="cl">      <span class="c1">//   });
</span></span></span><span class="line"><span class="cl">      <span class="c1">// },
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">editRecord</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">=</span> <span class="nx">item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">detailDialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">newRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">detailDialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">PanelListMain</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">ContactEdit</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;./ContactEdit&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>There&rsquo;s a lot going on, but we did not quite put in anything special.</p>
<p>To start with, we introduced ..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">makeFindMixin</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;feathers-vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">mixins</span><span class="o">:</span> <span class="p">[</span><span class="nx">makeFindMixin</span><span class="p">({</span> <span class="nx">service</span><span class="o">:</span> <span class="s2">&#34;contacts&#34;</span> <span class="p">})],</span>
</span></span></code></pre></div><p>The above code imported and used a feathers provided mixin that makes our coding easier and more generic. We have indicated our intent to use the <code>contacts</code> service - this refers to the service file that we created earlier, which also automatically &ldquo;maps&rdquo; (not exactly, but bear with me) to the <code>contacts</code> service in the backend Feathers.</p>
<p>We create a computed variable which can be used to pass params to the mixin -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">  <span class="nx">contactsParams</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span> <span class="nx">query</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">search</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span></code></pre></div><p>By doing just this, we have access to the <code>contacts</code> list. This is the list of contacts fetched from the backend and rendered as a table.</p>
<p>We can change <code>search</code> to pass search criteria. User enters search, and we just pass it as a param in a function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">search</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">srchStr</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">srchLastName</span><span class="p">)</span> <span class="nx">srchStr</span><span class="p">[</span><span class="s2">&#34;last_name&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">srchLastName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">srchStr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Again.. let me reiterate it for you.</p>
<p>All we did was to add a service, introduced boilerplate code for <code>feathers-client.js</code> and pulled in the mixin. This resulted in the contacts being pulled from the server / from our database.</p>
<p>In addition to what has been outlined so far -</p>
<ol>
<li>We can add functionality with simple param changes on client side and by adding hooks/custom services on server - again a lot of functionality with less code</li>
<li>The app has real-time functions - any changes (on server through API calls / by other users) are published to clients (all clients with default configuration) and our browser starts showing those changes</li>
</ol>
<p>What we did not do -</p>
<ul>
<li>use REST calls with something like fetch/ Axios</li>
<li>manage variables in components / Vuex</li>
<li>create logic to search - on client and server</li>
</ul>
<p>That&rsquo;s a lot of work saved.</p>
<h2 id="contact-edit">Contact Edit</h2>
<p>Let&rsquo;s keep going.</p>
<p>We will add a reference to a new component in <code>ContactList</code> to enable us to edit contacts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ContactEdit</span>
</span></span><span class="line"><span class="cl">  <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;detailDialog&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;detailDialog&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">:currentItem</span><span class="o">=</span><span class="s">&#34;currentItem&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>Also, add methods to display this detail component on click of <code>New</code> or <code>Edit</code> buttons.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">editRecord</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">=</span> <span class="nx">item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">detailDialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">newRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">=</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">detailDialog</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We set the current item and send it off to <code>ContactEdit</code>.</p>
<p>There are multiple, better ways to invoke <code>ContactEdit</code> to create more elegant/succinct code - we just used on of the options.</p>
<p><code>ContactEdit</code> has some interesting things going on-</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">v-dialog</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;showDialog&#34;</span> <span class="na">persistent</span> <span class="na">max-width</span><span class="o">=</span><span class="s">&#34;1100px&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">SubPanel</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">template</span> <span class="na">slot</span><span class="o">=</span><span class="s">&#34;toolbar-items&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;subtitle-2&#34;</span><span class="p">&gt;</span>New/Edit Contact<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">template</span> <span class="na">slot</span><span class="o">=</span><span class="s">&#34;content&#34;</span> <span class="na">v-if</span><span class="o">=</span><span class="s">&#34;item&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">FeathersVuexFormWrapper</span> <span class="na">:item</span><span class="o">=</span><span class="s">&#34;item&#34;</span> <span class="na">watch</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot</span><span class="o">=</span><span class="s">&#34;{ clone, save, reset, remove }&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">v-form</span> <span class="na">ref</span><span class="o">=</span><span class="s">&#34;form&#34;</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;validInput&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">v-card</span> <span class="na">flat</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">v-card-text</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;</span><span class="nt">v-text-field</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;item[&#39;first_name&#39;]&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;</span><span class="nt">v-text-field</span> <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;item[&#39;last_name&#39;]&#34;</span> <span class="na">required</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;/</span><span class="nt">v-text-field</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;</span><span class="nt">v-col</span> <span class="na">cols</span><span class="o">=</span><span class="s">&#34;12&#34;</span> <span class="na">md</span><span class="o">=</span><span class="s">&#34;3&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&lt;</span><span class="nt">v-select</span>
</span></span><span class="line"><span class="cl">                        <span class="na">:items</span><span class="o">=</span><span class="s">&#34;[&#39;active&#39;, &#39;inactive&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="na">label</span><span class="o">=</span><span class="s">&#34;Status&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="na">v-model</span><span class="o">=</span><span class="s">&#34;item[&#39;status_cd&#39;]&#34;</span>
</span></span><span class="line"><span class="cl">                        <span class="na">required</span>
</span></span><span class="line"><span class="cl">                      <span class="p">&gt;&lt;/</span><span class="nt">v-select</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&lt;/</span><span class="nt">v-col</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">v-row</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">v-card-text</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">v-card-actions</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-spacer</span><span class="p">&gt;&lt;/</span><span class="nt">v-spacer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">outlined</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;closeDialog&#34;</span><span class="p">&gt;</span>Cancel<span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;</span><span class="nt">v-btn</span> <span class="na">color</span><span class="o">=</span><span class="s">&#34;primary&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;saveRecord&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                    Save
</span></span><span class="line"><span class="cl">                  <span class="p">&lt;/</span><span class="nt">v-btn</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;/</span><span class="nt">v-card-actions</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;/</span><span class="nt">v-card</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">v-form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">FeathersVuexFormWrapper</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">SubPanel</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">v-dialog</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">mapMutations</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="nx">SubPanel</span> <span class="nx">from</span> <span class="s2">&#34;./layouts/SubPanel&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">import</span> <span class="p">{</span> <span class="nx">FeathersVuexFormWrapper</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;feathers-vuex&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">validInput</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">item</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">value</span><span class="o">:</span> <span class="nb">Boolean</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">currentItem</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">type</span><span class="o">:</span> <span class="nb">Object</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">required</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="p">{</span> <span class="nx">Contact</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">$FeathersVuex</span><span class="p">.</span><span class="nx">api</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">contactModel</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Contact</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">item</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">||</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">          <span class="o">?</span> <span class="nx">contactModel</span>
</span></span><span class="line"><span class="cl">          <span class="o">:</span> <span class="nx">Contact</span><span class="p">.</span><span class="nx">getFromStore</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">computed</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">showDialog</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">get</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">set</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="s2">&#34;input&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">SubPanel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span><span class="nx">mapMutations</span><span class="p">(</span><span class="s2">&#34;snackbar&#34;</span><span class="p">,</span> <span class="p">[</span><span class="s2">&#34;setSnack&#34;</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">saveRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">form</span><span class="p">.</span><span class="nx">validate</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">save</span><span class="p">().</span><span class="k">catch</span><span class="p">((</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="k">this</span><span class="p">.</span><span class="nx">showError</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span> <span class="k">else</span> <span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">closeDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">closeDialog</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">showDialog</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nx">showError</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// you can do other housekeeping here.
</span></span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="p">.</span><span class="nx">setSnack</span><span class="p">({</span> <span class="nx">message</span><span class="o">:</span> <span class="s2">&#34;Error saving contact.&#34;</span><span class="p">,</span> <span class="nx">color</span><span class="o">:</span> <span class="s2">&#34;error&#34;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error saving record&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>When the component loads, we specify whether we are creating a new record or editing an existing record.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">Contact</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">$FeathersVuex</span><span class="p">.</span><span class="nx">api</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">contactModel</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Contact</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">item</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span> <span class="o">||</span> <span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span> <span class="nx">contactModel</span>
</span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="nx">Contact</span><span class="p">.</span><span class="nx">getFromStore</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">currentItem</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>We fold our form within -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">FeathersVuexFormWrapper</span> <span class="na">:item</span><span class="o">=</span><span class="s">&#34;item&#34;</span> <span class="na">watch</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">template</span> <span class="na">v-slot</span><span class="o">=</span><span class="s">&#34;{ clone, save, reset, remove }&#34;</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">FeathersVuexFormWrapper</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>This code block does quite a few things -</p>
<ol>
<li>Automatically manage vuex best practices to clone our storage variable (<code>item</code> in our case) when necessary. We also indicated support saving, resetting form and removing records</li>
<li>Feathers client automatically maps the record received from the list to the context of the <code>Contact</code> service (either a new record or find existing record from store)</li>
</ol>
<p>Finally, we introduce code to save record -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">      <span class="nx">saveRecord</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">form</span><span class="p">.</span><span class="nx">validate</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">[</span><span class="s2">&#34;id&#34;</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">save</span><span class="p">().</span><span class="k">catch</span><span class="p">((</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="k">this</span><span class="p">.</span><span class="nx">showError</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span> <span class="k">else</span> <span class="k">this</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="k">this</span><span class="p">.</span><span class="nx">closeDialog</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span></code></pre></div><p>This code block validates form, and invokes <code>save</code> or <code>update</code> methods supported by our feathers-client to send data to our server. Server Feathers knows the operations from the invoked services and carries out those operations on server.</p>
<p>You may also observe that we did not quite use <code>auth</code> here, but it is implied in the background. Feathers figures out that for us. However, you will observe that we have created the <code>auth</code> module (all of two lines), included it as a plugin in the store, and created components for user registration and login.</p>
<p>That is it - you now have a full fledged.. oh wait.</p>
<p>Remember our previous statement? CRM application is not contacts alone - all of us and our neighbour&rsquo;s dog know that activities make CRM application &ldquo;whole&rdquo;.</p>
<p>That said - I don&rsquo;t want to take away that opportunity of creating a brand new service and Vuex plugin from you. Like the seers of yore, I just point to the direction and you execute all magic, eh?</p>
<h2 id="finis">Finis!</h2>
<p>So here we are then.. A complete.. , er.., 50% complete &ldquo;totally real-world application&rdquo; built using FeathersJS on server and client</p>
<p>It is time for you to spread your wings and fly to newer heights and into the next telephone pole with this knowledge.</p>
<p>I mentioned earlier that the code used here is <a href="https://github.com/techformist/featherlight-crm">available on Github</a> - please don&rsquo;t forget to <code>star</code> the repository. Those stars are what I count sometimes to go to sleep - more the merrier.</p>
<p>Enjoy.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Simple VueJS Starter Template</title>
      <link>https://techformist.com/vuejs-starter-template/</link>
      <pubDate>Wed, 15 Apr 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/vuejs-starter-template/</guid>
      <description>&lt;p&gt;VueJS booster template is a boilerplate that has simple features that make it &lt;em&gt;really&lt;/em&gt; easy to start new projects.&lt;/p&gt;
&lt;p&gt;Vue CLI makes starting a Vue project simple -&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vue create demo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This sets up everything that you need to start - but just so. There are more than few things missing for a functioning/easy-to-use project :)&lt;/p&gt;
&lt;h2 id=&#34;the-case-for-a-new-boilerplate&#34;&gt;The Case for a New Boilerplate&lt;/h2&gt;
&lt;p&gt;I take my role as a developer with every bit of smartness inherent in me. The problems faced with my back-end projects and my simple solution, &lt;a href=&#34;https://techformist.com/adonisjs-starter-template/&#34;&gt;booster project in AdonisJS&lt;/a&gt;, are very relevant to VueJS and other front-end experiments.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>VueJS booster template is a boilerplate that has simple features that make it <em>really</em> easy to start new projects.</p>
<p>Vue CLI makes starting a Vue project simple -</p>
<pre tabindex="0"><code>vue create demo
</code></pre><p>This sets up everything that you need to start - but just so. There are more than few things missing for a functioning/easy-to-use project :)</p>
<h2 id="the-case-for-a-new-boilerplate">The Case for a New Boilerplate</h2>
<p>I take my role as a developer with every bit of smartness inherent in me. The problems faced with my back-end projects and my simple solution, <a href="/adonisjs-starter-template/">booster project in AdonisJS</a>, are very relevant to VueJS and other front-end experiments.</p>
<p>In essence, I copy old projects to create something new to keep things as productive as possible.</p>
<p>Ignoring the philosophical implications of doing such a thing, there is this recurring problem of -</p>
<ol>
<li>Removing parts that are not relevant</li>
<li>Removing complexity for small/simple projects</li>
<li>Staying consistent to standards and practices</li>
<li>Trying to get all these messages to new developers (they exist and I don&rsquo;t like talking to them overnight to get them started)</li>
</ol>
<p>So it is that I created another simple starter template for VueJS that is useful to beginners and simple projects.</p>
<p>The idea is to keep things painfully simple and allow users to change as and when they need more complexity.</p>
<p>Here&rsquo;s presenting <a href="https://github.com/techformist/vue-vuetify-api-boost">vue-vuetify-api-boost</a>.</p>
<h2 id="what-does-the-starter-template-contain">What does the starter template contain?</h2>
<p>The standard set of components of this starter project/ boilerplate provide a consistent and standard experience while starting new projects. It consists of -</p>
<ol>
<li>Standard front-end library - Vuetify is used in this boilerplate</li>
<li>Consistent way of dealing with Vuex - Pathify FTW</li>
<li>Standard practices for them pesky API calls</li>
<li>Some screens to help get started</li>
</ol>
<h3 id="packages">Packages</h3>
<p>We use the below standard features of Vue CLI-</p>
<ol>
<li>Vue + Vuex + Vue Router</li>
<li>Node SaSS</li>
<li>Linter</li>
</ol>
<p>The major additional packages beyond those installed by Vue CLI installed -</p>
<ul>
<li><code>vuetify</code>: the most popular Vue styling library</li>
<li><code>axios</code>: API calls</li>
<li><code>vuex-persist</code>: Persist vuex to local storage and cookies (using <code>js-cookie</code>)</li>
<li><code>vue-html-to-paper</code>: allow pretty printing of pages (no example pages)</li>
<li><code>file-saver</code>: Save files being downloaded from server (no example pages)</li>
<li><code>vue2-dropzone</code>: Drop files to upload (no example pages)</li>
<li><code>v-tooltip</code>: Tooltips that don&rsquo;t need a 100 lines to implement</li>
<li>[dev] <code>prerender-spa-plugin</code>: pre-render pages without selling your soul</li>
</ul>
<h3 id="key-features">Key Features</h3>
<ul>
<li>Registration and login pages</li>
<li>Router setup with protected routes, and dynamically loaded components</li>
<li>Standard <code>Terms</code>, <code>Privacy Policy</code> and <code>Credit</code> pages</li>
<li>Default and standard layouts for list and detail views (of a CRUD application)</li>
<li>A sample <code>Service Request</code> entity that demonstrates how fetch/insert/update work</li>
</ul>
<h3 id="structure">Structure</h3>
<p>The structure is plain and simple.</p>
<h4 id="views-and-components">Views and components</h4>
<p>UI elements are like any other project. We use Vuetify for styling!</p>
<h4 id="layouts">Layouts</h4>
<p>Layouts exist in a separate folder under components. Every view starts with a layout and utilises the slots provided by layout to paint the pretty picture.</p>
<h4 id="mixin">Mixin</h4>
<p>There is a single mixin that provides validations and a few utilities used across the board. You have to import the mixin in every component you use. It is deliberately not included for the entire app - though you could do that.</p>
<h4 id="router">Router</h4>
<p>Router is as simple as they come. It dynamically imports most components to avoid front-loading every single component at start-up.</p>
<p>It also provides protected routes and dynamic component loading in a couple of cases.</p>
<h4 id="vuex">Vuex</h4>
<ul>
<li><code>mutations</code>, <code>getters</code>, <code>actions</code> exist in <code>store/*</code> themselves. They can be segregated into separate files if projects become too complex</li>
<li>There is no distinct <code>service</code> layer. I understand (and agree to) the arguments to keep business logic separate from <code>vuex</code> logic, but I don&rsquo;t use those in simpler projects.</li>
<li><code>vuex-pathify</code> takes care of all mutations - so, few mutations are hand-written</li>
</ul>
<h2 id="using-the-boilerplate">Using the boilerplate</h2>
<p>Clone this repository to start..</p>
<pre tabindex="0"><code>git clone https://github.com/techformist/vue-vuetify-api-boost
</code></pre><p>Install packages -</p>
<pre tabindex="0"><code>cd vue-vuetify-api-boost
npm i
</code></pre><h2 id="project-setup">Project setup</h2>
<p>Setup like any other Vue project.</p>
<ul>
<li>Add/ change views and components</li>
<li>Change layouts</li>
<li>.. and more</li>
</ul>
<p>Compile with hot-reload for development</p>
<pre tabindex="0"><code>npm run serve
</code></pre><p>Voila, we have this super screen :)</p>
<p><img loading="lazy" src="/misc/vue-booster-template.jpg" type="" alt="vue-booster-template"  /></p>
<p>You can continue using Vue CLI with this project.</p>
<h2 id="back-end-partner-projects">Back-end Partner Projects</h2>
<p>The template runs without issues out-of-the-box with the following projects in back-end -</p>
<ul>
<li><a href="/adonisjs-starter-template/">booster project in AdonisJS</a></li>
</ul>
<h2 id="other-boilerplates">Other Boilerplates</h2>
<p>See -</p>
<ul>
<li><a href="https://github.com/chrisvfritz/vue-enterprise-boilerplate">Vue Enterprise Boilerplate</a>: for large projects / teams</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>None of the additional packages or features in the starter template add anything significantly new, but required at least a day to set everything up.</p>
<p>Hopefully, we continue optimising the start-up time for our projects and eliminate more boring tasks required at the very beginning of a project. And.. add even more stuff useful to ourselves and anyone else who bothers picking up this boilerplate amongst a million other (better) templates available on the Internet.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
