<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Vue.js - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Vue.js - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 19 May 2026 04:43:13 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/vuejs/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Learn Vue.js – JavaScript Framework Course ]]>
                </title>
                <description>
                    <![CDATA[ Vue.js is a progressive JavaScript framework for building user interfaces and single-page applications. Loved for its simplicity, flexibility, and performance, it allows developers to start small and scale up to complex applications with ease. Whethe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-vuejs-javascript-framework-course/</link>
                <guid isPermaLink="false">691228d49872f01b424e3353</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Mon, 10 Nov 2025 18:03:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762797723327/7e67725a-1f0c-48fa-ad5c-173495b0ffeb.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Vue.js is a progressive JavaScript framework for building user interfaces and single-page applications. Loved for its simplicity, flexibility, and performance, it allows developers to start small and scale up to complex applications with ease. Whether you’re creating quick prototypes or production-ready projects, Vue’s approachable syntax and strong community make it an excellent tool to have in your toolkit.</p>
<p>We just posted a course on the freeCodeCamp.org YouTube channel that will teach you Vue as you build real projects, dive into its core features, and create dynamic, reusable, and reactive apps with ease. Rachel Johnson from Scrimba developed this course.  </p>
<p>The course is divided into two sections. Here is what is covered in each section.</p>
<p><strong>Section 1:</strong></p>
<ul>
<li><p>Learn what Vue is and why it’s so popular</p>
</li>
<li><p>Get set up with both CDN and local installs</p>
</li>
<li><p>Create your first apps</p>
</li>
<li><p>Explore the structure of a Vue project so you know where everything lives</p>
</li>
</ul>
<p><strong>Section 2:</strong></p>
<ul>
<li><p>Work with Vue’s core building blocks: components, reactivity, template syntax</p>
</li>
<li><p>Handle images and assets</p>
</li>
<li><p>Split your app into reusable parts</p>
</li>
<li><p>Bind data dynamically</p>
</li>
<li><p>Display content that updates instantly</p>
</li>
<li><p>A simple Vue Facts app</p>
</li>
</ul>
<p>Watch the full course <a target="_blank" href="https://youtu.be/Kt2E8nblvXU">on the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Kt2E8nblvXU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Vue Composables Work – Explained with Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ Vue composables are a very helpful tool when developing Vue applications. They give developers an easy way to reuse logic across our applications. In addition to allowing for “stateless” logic (things like formatting or routine calculations), composa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-vue-composables-work/</link>
                <guid isPermaLink="false">684c58453ee20021234073af</guid>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Brian Barrow ]]>
                </dc:creator>
                <pubDate>Fri, 13 Jun 2025 16:56:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749830866913/e732db46-638b-42cd-aabf-ad51a54a3409.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Vue composables are a very helpful tool when developing Vue applications. They give developers an easy way to reuse logic across our applications. In addition to allowing for “stateless” logic (things like formatting or routine calculations), composables also give us the ability to reuse stateful logic throughout the app.</p>
<p>Before diving into the tutorial below, I want to mention that the documentation for Vue is <em>really</em> good. <a target="_blank" href="https://vuejs.org/guide/reusability/composables">The page on composables</a> explains the basics really well and will get you 90 percent of the way there. I am writing this article because I think the examples in the docs could go a little deeper in explaining how things can work inside of a composable. I will be reiterating some of the information from the docs, but I will also provide an example of a more complex composable.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-use-composables">Why Use Composables?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-simple-composable-example">Simple Composable Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-complex-composable-example">Complex Composable Example</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-util-function-for-fetch">Util function for fetch</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-useasyncstate-composable">useAsyncState Composable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-usage-in-a-component">Usage in a component</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-use-composables">Why Use Composables?</h2>
<p>Composables let you reuse stateful logic across your apps. Whenever there is logic that is used in more than two places, we typically want to pull that logic into its own function. Most of the time, that logic is considered “stateless”, meaning that it takes an input and returns an output. The docs mention date formatting, but this could also include something like currency calculations or string validation.</p>
<p>In modern web applications, there are often pieces of logic that require managing state over time. Inside a typical component, we have the ability to adapt the application depending on the “state” of different variables within the component. Sometimes that logic, or at least pieces of that logic, are reused throughout the app.</p>
<p>For example, in an e-commerce application, you might have logic to increase and decrease the quantity of a product a person is adding to their cart. This logic could be used both on the product page, and inside the cart itself.</p>
<p>The look and feel of both those places will be different, so re-using a full component wouldn’t make sense – but we still want to centralize the logic to make the code easier to maintain. That is where Composables come in.</p>
<p>(It is worth noting that not everything needs to be a composable. Logic that is only used in a single component shouldn’t be refactored into a composable until necessary.)</p>
<h2 id="heading-simple-composable-example">Simple Composable Example</h2>
<p>Let’s take a look at a simple counter example. Here is some code for a very simple <code>Counter</code> component.</p>
<pre><code class="lang-typescript">&lt;script setup lang=<span class="hljs-string">"ts"</span>&gt;
  <span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>

  <span class="hljs-keyword">const</span> count: Ref&lt;<span class="hljs-built_in">number</span>&gt; = ref(<span class="hljs-number">0</span>)
  <span class="hljs-keyword">const</span> increment = <span class="hljs-function">() =&gt;</span> {
    count.value++
  }
  <span class="hljs-keyword">const</span> decrement = <span class="hljs-function">() =&gt;</span> {
    count.value--
  }
&lt;/script&gt;

&lt;template&gt;
  &lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"bg-teal-100 border-2 border-gray-800 rounded-xl p-4 w-64"</span>&gt;
    &lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"text-center mb-4"</span>&gt;
      &lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">"text-lg font-medium text-gray-800"</span>&gt;Count: {{ count }}&lt;/span&gt;
    &lt;/div&gt;

    &lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"flex gap-2 justify-center"</span>&gt;
      &lt;button
        <span class="hljs-meta">@click</span>=<span class="hljs-string">"decrement"</span>
        <span class="hljs-keyword">class</span>=<span class="hljs-string">"bg-red-100 border-2 border-gray-800 rounded px-4 py-0 text-gray-800 font-medium hover:bg-red-500 transition-colors"</span>
      &gt;
        -
      &lt;/button&gt;

      &lt;button
        <span class="hljs-meta">@click</span>=<span class="hljs-string">"count = 0"</span>
        <span class="hljs-keyword">class</span>=<span class="hljs-string">"bg-gray-100 border-2 border-gray-800 rounded px-4 py-0 text-gray-800 font-medium hover:bg-gray-300 transition-colors"</span>
      &gt;
        Reset
      &lt;/button&gt;

      &lt;button
        <span class="hljs-meta">@click</span>=<span class="hljs-string">"increment"</span>
        <span class="hljs-keyword">class</span>=<span class="hljs-string">"bg-green-100 border-2 border-gray-800 rounded px-4 py-0 text-gray-800 font-medium hover:bg-green-500 transition-colors"</span>
      &gt;
        +
      &lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>The output of that component would look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748355723167/8c39759c-6fd9-4fcf-abdf-f67e672c172f.gif" alt="8c39759c-6fd9-4fcf-abdf-f67e672c172f" class="image--center mx-auto" width="310" height="178" loading="lazy"></p>
<p>This works great, but if we end up needing this same counter logic in another component with a completely different look and feel, then we would end up repeating the logic. We can extract the logic into a composable and access the same stateful logic anywhere we need to.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// counter.ts</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useCounter</span>(<span class="hljs-params"></span>): <span class="hljs-title">Readonly</span>&lt;</span>{
  count: Ref&lt;<span class="hljs-built_in">number</span>&gt;
  increment: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>
  decrement: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>
}&gt; {
  <span class="hljs-keyword">const</span> count: Ref&lt;<span class="hljs-built_in">number</span>&gt; = ref(<span class="hljs-number">0</span>)
  <span class="hljs-keyword">const</span> increment = <span class="hljs-function">() =&gt;</span> {
    count.value++
  }
  <span class="hljs-keyword">const</span> decrement = <span class="hljs-function">() =&gt;</span> {
    count.value--
  }
  <span class="hljs-keyword">return</span> { count, increment, decrement }
}
</code></pre>
<p>Then we update the script tag in the component to use the composable:</p>
<pre><code class="lang-typescript">&lt;script setup&gt;
<span class="hljs-keyword">import</span> { useCounter } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/counter.ts'</span>

<span class="hljs-keyword">const</span> { count, increment, decrement } = useCounter()
&lt;/script&gt;

&lt;template&gt;
  ...
&lt;/template&gt;
</code></pre>
<p>Now we can use this logic in multiple components throughout the app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748355833182/d90d000e-f309-4b22-9530-8e4614b450ec.gif" alt="d90d000e-f309-4b22-9530-8e4614b450ec" class="image--center mx-auto" width="774" height="338" loading="lazy"></p>
<p>You will notice that only the logic is copied and each component still has its own copy of the <code>count</code> state. Using a composable does not mean the state is shared across components, only the stateful logic.</p>
<h2 id="heading-complex-composable-example">Complex Composable Example</h2>
<p>In the Vue docs, they give an example of using a composable to handle async data fetching. There are a couple of issues I have with the example they give. The main one is that the error handling is not robust for real world applications. Given that they just want to showcase a straightforward use of composables, this is understandable. But I wanted to showcase a more realistic implementation.</p>
<h3 id="heading-util-function-for-fetch">Util function for <code>fetch</code></h3>
<p>Before getting into the composable, we need to set up a util function for the <code>fetch</code> API. This is because we want to make sure every request throws an error if it fails. The <code>fetch</code> API doesn’t throw an error if the request responds with an error status. We have to check the <code>response.ok</code> in order to verify the status, and then throw an error if necessary.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// utils.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleFetch</span>(<span class="hljs-params">url: <span class="hljs-built_in">string</span>, options: RequestInit = {}</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Response</span>&gt; </span>{
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url, options)
  <span class="hljs-keyword">if</span> (!res.ok) {
    <span class="hljs-keyword">const</span> err = <span class="hljs-keyword">await</span> res.text()
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
  }
  <span class="hljs-keyword">return</span> res
}
</code></pre>
<h3 id="heading-useasyncstate-composable">useAsyncState Composable</h3>
<p>When working with async state, the requests can be in a few different states:</p>
<ul>
<li><p>Pending</p>
</li>
<li><p>Resolved</p>
</li>
<li><p>Rejected</p>
</li>
</ul>
<p>In addition to these states, we want to track the data or the error that comes back from the request.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// useAsyncState.ts</span>
<span class="hljs-keyword">import</span> { shallowRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>

<span class="hljs-comment">// Specify a type for the response</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> AsyncState&lt;T&gt; = {
  data: Ref&lt;T | <span class="hljs-literal">null</span>&gt;
  error: Ref&lt;<span class="hljs-built_in">Error</span> | <span class="hljs-literal">null</span>&gt;
  isPending: Ref&lt;<span class="hljs-built_in">boolean</span>&gt;
  isResolved: Ref&lt;<span class="hljs-built_in">boolean</span>&gt;
  isRejected: Ref&lt;<span class="hljs-built_in">boolean</span>&gt;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useAsyncState</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">promise: <span class="hljs-built_in">Promise</span>&lt;T&gt;</span>): <span class="hljs-title">AsyncState</span>&lt;<span class="hljs-title">T</span>&gt; </span>{
  <span class="hljs-comment">// I used shallowRef instead of ref to avoid deep reactivity</span>
  <span class="hljs-comment">// I only care about the top-level properties being reactive</span>
  <span class="hljs-keyword">const</span> data = shallowRef&lt;T | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>)
  <span class="hljs-keyword">const</span> error = shallowRef&lt;<span class="hljs-built_in">Error</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>)
  <span class="hljs-keyword">const</span> isPending = shallowRef(<span class="hljs-literal">false</span>)
  <span class="hljs-keyword">const</span> isResolved = shallowRef(<span class="hljs-literal">false</span>)
  <span class="hljs-keyword">const</span> isRejected = shallowRef(<span class="hljs-literal">false</span>)

  data.value = <span class="hljs-literal">null</span>
  error.value = <span class="hljs-literal">null</span>
  isPending.value = <span class="hljs-literal">true</span>
  isRejected.value = <span class="hljs-literal">false</span>
  isResolved.value = <span class="hljs-literal">false</span>

  promise.then(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
    data.value = result
    isPending.value = <span class="hljs-literal">false</span>
    isResolved.value = <span class="hljs-literal">true</span>
  }).catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
    error.value = err
    isPending.value = <span class="hljs-literal">false</span>
    isRejected.value = <span class="hljs-literal">true</span>
  })

  <span class="hljs-keyword">return</span> { data, error, isPending, isResolved, isRejected }
}
</code></pre>
<p>This gives a few more explicit properties for the different states, rather than relying on the values in <code>data</code> and <code>error</code>. You’ll also notice that this composable takes in a promise rather than a URL string like the docs show. Different endpoints will have different response types and I wanted to be able to handle those outside of this composable.</p>
<h3 id="heading-usage-in-a-component">Usage in a component</h3>
<p>I have set up an endpoint that will wait a random number of seconds before responding either successfully or with an error. My component is calling this endpoint using the composable and using the data from the composable to update the template.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748364641116/304b8c08-5277-4243-b621-70a7c19edcfd.gif" alt="304b8c08-5277-4243-b621-70a7c19edcfd" class="image--center mx-auto" width="702" height="894" loading="lazy"></p>
<p>With the error state showing like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748364674070/7d0c6923-85b9-4971-8f69-d127ffa6c1f4.png" alt="7d0c6923-85b9-4971-8f69-d127ffa6c1f4" class="image--center mx-auto" width="724" height="912" loading="lazy"></p>
<p>You can see a working example at <a target="_blank" href="https://understanding-composables.pages.dev/">https://understanding-composables.pages.dev/</a>.</p>
<p>To make this a bit easier to explain and understand, I am breaking up the <code>&lt;script&gt;</code> tag and the <code>&lt;template&gt;</code> sections of the component.</p>
<h4 id="heading-script">Script</h4>
<pre><code class="lang-typescript">&lt;script lang=<span class="hljs-string">"ts"</span> setup&gt;
<span class="hljs-keyword">import</span> { ref, unref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> { useAsyncState } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/composables'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { AsyncState } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/composables'</span>
<span class="hljs-keyword">import</span> { handleFetch } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/utils'</span>

<span class="hljs-keyword">interface</span> RandomResponse {
  msg: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRandomResponse</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">RandomResponse</span>&gt; </span>{
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> handleFetch(<span class="hljs-string">'https://briancbarrow.com/api/random'</span>)
  <span class="hljs-keyword">const</span> text = <span class="hljs-keyword">await</span> response.text()
  <span class="hljs-keyword">return</span> { msg: text }
}

<span class="hljs-keyword">const</span> randomResponseData: Ref&lt;AsyncState&lt;RandomResponse&gt; | <span class="hljs-literal">null</span>&gt; = ref(<span class="hljs-literal">null</span>)

<span class="hljs-keyword">const</span> handleMakeRequest = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> data = getRandomResponse()
    randomResponseData.value = useAsyncState(data)
}
&lt;/script&gt;
</code></pre>
<p>Here we have a method, <code>getRandomResponse</code> that calls an endpoint and returns a promise. That promise is then passed into the <code>useAsyncState</code> when <code>handleMakeRequest</code> is called. That puts the full return value into the <code>randomResponseData</code> ref which we can then use inside the template.</p>
<p>Rather than show the full template, I will just show a few portions of it.</p>
<p>Here you can see two different buttons being used depending on the state. I am using a separate button element to indicate the “loading” state, but in practice you can use the composable properties to set the <code>disabled</code> property of the button and change the text.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
          <span class="hljs-attr">v-if</span>=<span class="hljs-string">"
            !randomResponseData?.isPending &amp;&amp;
            !randomResponseData?.error &amp;&amp;
            !randomResponseData?.data
          "</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"</span>
          @<span class="hljs-attr">click</span>=<span class="hljs-string">"handleMakeRequest"</span>
        &gt;</span>
          Make Request
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Loading State Button --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
          <span class="hljs-attr">v-if</span>=<span class="hljs-string">"randomResponseData?.isPending"</span>
          <span class="hljs-attr">disabled</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 bg-blue-600 text-white font-medium rounded-lg opacity-75 cursor-not-allowed flex items-center mx-auto"</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"animate-spin -ml-1 mr-3 h-5 w-5 text-white"</span>
            <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
            <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
            <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"opacity-25"</span>
              <span class="hljs-attr">cx</span>=<span class="hljs-string">"12"</span>
              <span class="hljs-attr">cy</span>=<span class="hljs-string">"12"</span>
              <span class="hljs-attr">r</span>=<span class="hljs-string">"10"</span>
              <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
              <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"4"</span>
            &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">circle</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"opacity-75"</span>
              <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
              <span class="hljs-attr">d</span>=<span class="hljs-string">"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"</span>
            &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
          Loading...
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Here are a couple of the rows from the table:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"divide-x divide-gray-200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-4 pr-4 pl-4 text-sm font-medium whitespace-nowrap text-gray-900 sm:pl-0"</span>&gt;</span>
    isPending
  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 text-sm whitespace-nowrap text-gray-500"</span>
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"randomResponseData?.isPending ? 'bg-blue-500' : 'bg-gray-300'"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 text-sm whitespace-nowrap text-gray-500"</span>&gt;</span>
    {{ randomResponseData?.isPending }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"divide-x divide-gray-200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-4 pr-4 pl-4 text-sm font-medium whitespace-nowrap text-gray-900 sm:pl-0"</span>&gt;</span>
    data
  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 text-sm whitespace-nowrap text-gray-500"</span>
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"randomResponseData?.data ? 'bg-green-500' : 'bg-gray-300'"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-4 text-sm whitespace-nowrap text-gray-500"</span>&gt;</span>
    {{ unref(randomResponseData?.data)?.msg }}
  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
</code></pre>
<p>In those <code>tr</code> tags, you can see the template rendering different things depending on the state coming from the composable.</p>
<p>For a more complete look at the code, you can visit <a target="_blank" href="https://github.com/briancbarrow/understanding-composables">the GitHub repo</a>. You can also look at how VueUse, a collection of composables, handles similar functionality: <a target="_blank" href="https://vueuse.org/core/useAsyncState/">https://vueuse.org/core/useAsyncState/</a></p>
<p>In a future article, I’ll dive into their implementation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Composables are an incredibly useful tool in Vue 3. As projects grow in size and scope, knowing how and when to use composables can improve the maintainability of the project over the long term.</p>
<p>The key is identifying when you have stateful logic that needs to be reused across components, then extracting it into a well-structured composable that handles edge cases properly.</p>
<p>For more real world examples you can check out the <a target="_blank" href="https://vueuse.org/">VueUse library</a> and <a target="_blank" href="https://github.com/vueuse/vueuse">repo</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Vue.js in This Beginner's Course ]]>
                </title>
                <description>
                    <![CDATA[ If you’ve been meaning to add Vue.js to your front-end toolbelt, today’s your day. We just published a full, 6-hour Vue 3 course on the freeCodeCamp.org YouTube channel. Instructor Bhrugen Patel walks you through everything you need to build modern, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-vuejs-in-this-beginners-course/</link>
                <guid isPermaLink="false">682f84537a13a40bfa336307</guid>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 22 May 2025 20:08:51 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747944784614/489ca833-5acc-48ec-b96a-bc876ce43563.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’ve been meaning to add Vue.js to your front-end toolbelt, today’s your day. We just published a full, 6-hour Vue 3 course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel. Instructor Bhrugen Patel walks you through everything you need to build modern, production-ready Vue apps, from the very first <code>{{ mustache }}</code> to scalable state management with Pinia.</p>
<h2 id="heading-why-vuejs">Why Vue.js?</h2>
<p>Vue stands out in the frontend landscape for several compelling reasons. Its gentle learning curve makes it accessible for beginners while offering depth for experts. As a progressive framework, you can adopt Vue incrementally - use just the view layer or leverage the full ecosystem. Vue delivers exceptional performance with its lightweight virtual DOM implementation and minimal overhead. Vue 3's composition API provides fine-grained reactivity that's both powerful and intuitive. The framework works equally well for small widgets or enterprise-scale applications. Vue offers robust tooling with Vue CLI, DevTools, and official libraries for routing and state management. You'll benefit from active development, regular updates, and extensive third-party resources from the vibrant community.</p>
<h2 id="heading-course-overview">Course Overview</h2>
<p>Bhrugen guides you through Vue's powerful ecosystem, covering essential concepts like reactivity, directives, and component architecture before advancing to routing, API integration, and state management with Pinia.</p>
<p>The curriculum progresses logically through Vue Fundamentals with core concepts and setup, Reactivity for understanding Vue's responsive data system, Directives &amp; Styling for manipulating the DOM declaratively, and Events &amp; Data Binding for creating interactive interfaces. You'll learn about Forms for building and validating user inputs, Component Architecture for creating reusable UI elements, and Component Communication using props, events, and advanced patterns.</p>
<p>The course covers Provide/Inject for dependency injection across components, Slots &amp; Lifecycle Hooks for template composition and timing control, Routing for single-page application navigation, API Integration for connecting to backend services, and State Management with Pinia for global state.</p>
<p>Watch the full course on the <a target="_blank" href="https://youtu.be/8pn9KEuXG28">freeCodeCamp.org YouTube channel</a> (7-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/8pn9KEuXG28" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Event Handling Works in Vue 3: A Guide for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Event handling in Vue 3 allows developers to respond to user interactions like clicks, key presses, form submissions, and more. Vue provides simple and flexible ways to manage these interactions, enabling you to build dynamic and engaging application... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-event-handling-works-in-vue-3-guide-for-devs/</link>
                <guid isPermaLink="false">66e1d18aee859f8ea9224bdb</guid>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ event handling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Asfak Ahmed ]]>
                </dc:creator>
                <pubDate>Wed, 11 Sep 2024 17:21:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725980520061/87728aa1-f3c5-451d-9f11-5163f527d029.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Event handling in Vue 3 allows developers to respond to user interactions like clicks, key presses, form submissions, and more.</p>
<p>Vue provides simple and flexible ways to manage these interactions, enabling you to build dynamic and engaging applications.</p>
<h3 id="heading-in-this-guide-well-cover">In this guide, we'll cover:</h3>
<ul>
<li><p>Basic event handling (for example, <code>click</code> events)</p>
</li>
<li><p>Event modifiers like <code>.prevent</code>, <code>.once</code>, and <code>.stop</code></p>
</li>
<li><p>Custom events between parent and child components</p>
</li>
<li><p>Handling events in forms</p>
</li>
<li><p>Keyboard events</p>
</li>
<li><p>The basics of <code>emit</code></p>
</li>
<li><p>The basics of <code>v-model</code></p>
</li>
</ul>
<p>By the end, you'll be able to handle a wide range of events and improve user interaction in your Vue applications.</p>
<h3 id="heading-basic-event-handling">Basic Event Handling</h3>
<p>Vue makes it easy to handle basic events like <code>click</code>, <code>input</code>, and <code>submit</code> directly in your template. You can use the <code>@</code> symbol (short for <code>v-on</code>) to listen for events on DOM elements.</p>
<h4 id="heading-example-handling-a-click-event">Example: Handling a Click Event</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"handleClick"</span>&gt;</span>Click Me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> message = ref(<span class="hljs-string">'Hello, Vue 3!'</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Button Clicked!'</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation">Code explanation:</h4>
<ul>
<li><p><code>@click="handleClick"</code>: The <code>@</code> symbol is shorthand for <code>v-on</code>. It listens for the <code>click</code> event and calls the <code>handleClick</code> method when the button is clicked.</p>
</li>
<li><p><code>message.value = 'Button Clicked!'</code>: In Vue 3's Composition API, <code>ref</code> creates reactive variables. When the button is clicked, the <code>message</code> reacts to variable updates, and the change is reflected in the DOM automatically.</p>
</li>
</ul>
<p>This simple mechanism of listening to events and binding methods is foundational to handling user interactions in Vue.</p>
<h3 id="heading-event-modifiers">Event Modifiers</h3>
<p>Vue event modifiers allow you to control how events are handled, preventing default behavior or stopping propagation, for example. Common event modifiers include <code>.prevent</code>, <code>.stop</code>, <code>.once</code>, <code>.capture</code>, and <code>.passive</code>.</p>
<h4 id="heading-1-the-prevent-modifier">1. The <code>.prevent</code> Modifier</h4>
<p>The <code>.prevent</code> modifier calls <code>event.preventDefault()</code>, preventing the default behavior of events like form submission.</p>
<h5 id="heading-example-using-prevent-to-handle-form-submission">Example: Using <code>.prevent</code> to Handle Form Submission</h5>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"handleSubmit"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"inputValue"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ output }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> inputValue = ref(<span class="hljs-string">''</span>);
<span class="hljs-keyword">const</span> output = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleSubmit</span>(<span class="hljs-params"></span>) </span>{
  output.value = <span class="hljs-string">`Form submitted with value: <span class="hljs-subst">${inputValue.value}</span>`</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-1">Code explanation:</h4>
<ul>
<li><p><code>@submit.prevent</code>: Prevents the form from refreshing the page when submitted, allowing the <code>handleSubmit</code> function to process the form data instead.</p>
</li>
<li><p><code>v-model="inputValue"</code>: Two-way data binding between the form input and the <code>inputValue</code> reactive variable. It updates in real time as the user types.</p>
</li>
</ul>
<p><strong>When to use</strong> <code>.prevent</code><strong>:</strong> Use <code>.prevent</code> when handling forms or other elements where you want to prevent the default behavior, such as preventing links from navigating.</p>
<h4 id="heading-2-the-stop-modifier">2. The <code>.stop</code> Modifier</h4>
<p>The <code>.stop</code> modifier calls <code>event.stopPropagation()</code>, preventing the event from bubbling up to parent elements.</p>
<h5 id="heading-example-prevent-event-bubbling">Example: Prevent Event Bubbling</h5>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"handleDivClick"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click.stop</span>=<span class="hljs-string">"handleButtonClick"</span>&gt;</span>Click Me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> message = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleDivClick</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Div clicked!'</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleButtonClick</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Button clicked!'</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-2">Code explanation:</h4>
<ul>
<li><code>.stop</code>: Clicking the button only triggers <code>handleButtonClick</code> and prevents the click from propagating to the parent <code>div</code>. Without <code>.stop</code>, clicking the button would also trigger <code>handleDivClick</code>.</li>
</ul>
<p><strong>When to use</strong> <code>.stop</code><strong>:</strong> Use it to prevent parent elements from reacting to child element events.</p>
<h4 id="heading-3-the-once-modifier">3. The <code>.once</code> Modifier</h4>
<p>The <code>.once</code> modifier ensures that the event listener is only called once.</p>
<h5 id="heading-example-handling-a-click-event-once">Example: Handling a Click Event Once</h5>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click.once</span>=<span class="hljs-string">"handleClickOnce"</span>&gt;</span>Click Me Once<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> message = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClickOnce</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Button clicked once!'</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-3">Code explanation:</h4>
<ul>
<li><code>.once</code>: The <code>handleClickOnce</code> method is triggered the first time the button is clicked. Subsequent clicks do nothing because the event listener is removed after the first execution.</li>
</ul>
<p><strong>When to use</strong> <code>.once</code><strong>:</strong> Use it for actions that should only happen once, such as a one-time form submission.</p>
<h4 id="heading-4-the-capture-modifier">4. The <code>.capture</code> Modifier</h4>
<p>The <code>.capture</code> modifier makes the event handler trigger during the capture phase rather than the bubbling phase.</p>
<h5 id="heading-example-handling-an-event-in-the-capture-phase">Example: Handling an Event in the Capture Phase</h5>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> @<span class="hljs-attr">click.capture</span>=<span class="hljs-string">"handleClickCapture"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"handleClickButton"</span>&gt;</span>Click Me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> message = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClickCapture</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Click event captured!'</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClickButton</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Button clicked!'</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-4">Code explanation:</h4>
<ul>
<li><code>.capture</code>: The click on the parent <code>div</code> is handled first, before the child button’s click event, because the <code>capture</code> phase happens before the bubbling phase.</li>
</ul>
<p><strong>When to use</strong> <code>.capture</code><strong>:</strong> Useful when you need to intercept an event before it reaches its target.</p>
<h3 id="heading-custom-events">Custom Events</h3>
<p>In Vue, child components can emit custom events to communicate with parent components. This pattern is commonly used to pass data or trigger methods in parent components.</p>
<h4 id="heading-example-emitting-and-handling-custom-events">Example: Emitting and Handling Custom Events</h4>
<p><code>ParentComponent.vue</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ChildComponent</span> @<span class="hljs-attr">custom-event</span>=<span class="hljs-string">"handleCustomEvent"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ parentMessage }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;
<span class="hljs-keyword">import</span> ChildComponent <span class="hljs-keyword">from</span> <span class="hljs-string">'./ChildComponent.vue'</span>;

<span class="hljs-keyword">const</span> parentMessage = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleCustomEvent</span>(<span class="hljs-params">payload</span>) </span>{
  parentMessage.value = <span class="hljs-string">`Received custom event with payload: <span class="hljs-subst">${payload}</span>`</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p><code>ChildComponent.vue</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"emitCustomEvent"</span>&gt;</span>Emit Custom Event<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { defineEmits } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> emit = defineEmits();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">emitCustomEvent</span>(<span class="hljs-params"></span>) </span>{
  emit(<span class="hljs-string">'custom-event'</span>, <span class="hljs-string">'Hello from ChildComponent'</span>);
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-5">Code explanation:</h4>
<ul>
<li><p><code>defineEmits()</code>: This is used in the child component to define custom events. Here, the child emits a <code>custom-event</code> with a payload of <code>'Hello from ChildComponent'</code>. (<a target="_blank" href="https://asfakahmedsblog.hashnode.dev/understanding-vuejs-emit-a-complete-guide">you can learn more details of emit from here</a>)</p>
</li>
<li><p><strong>Event Handling in Parent</strong>: The parent component listens for <code>custom-event</code> and responds by updating its <code>parentMessage</code> with the event payload.</p>
</li>
</ul>
<p><strong>When to use custom events:</strong> Use them for communication between parent and child components, especially for passing data from child to parent.</p>
<h3 id="heading-event-handling-in-forms">Event Handling in Forms</h3>
<p>Vue’s <code>v-model</code> simplifies handling form inputs by creating two-way data binding between the form field and a data variable.</p>
<h4 id="heading-example-handling-input-and-form-submission">Example: Handling Input and Form Submission</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"handleSubmit"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"formData.name"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Name"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"formData.email"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ formOutput }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> formData = ref({ <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">''</span> });
<span class="hljs-keyword">const</span> formOutput = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleSubmit</span>(<span class="hljs-params"></span>) </span>{
  formOutput.value = <span class="hljs-string">`Submitted Name: <span class="hljs-subst">${formData.value.name}</span>, Email: <span class="hljs-subst">${formData.value.email}</span>`</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-6">Code explanation:</h4>
<ul>
<li><p><code>v-model="</code><a target="_blank" href="http://formData.name"><code>formData.name</code></a><code>"</code>: This binds the input field directly to the <a target="_blank" href="http://formData.name"><code>formData.name</code></a> variable, allowing automatic updates as the user types. (<a target="_blank" href="https://asfakahmedsblog.hashnode.dev/understanding-vuejs-v-model-a-complete-guide">you can learn more details of v-model from here</a>)</p>
</li>
<li><p>The <code>handleSubmit</code> method processes the form data and displays it in the paragraph below the form.</p>
</li>
</ul>
<h3 id="heading-keyboard-events">Keyboard Events</h3>
<p>Vue also makes it easy to handle keyboard events such as <code>keydown</code>, <code>keyup</code>, and <code>keypress</code>.</p>
<h4 id="heading-example-handling-keyboard-events">Example: Handling Keyboard Events</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> @<span class="hljs-attr">keydown.enter</span>=<span class="hljs-string">"handleEnterKey"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Press Enter"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ message }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">setup</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;

<span class="hljs-keyword">const</span> message = ref(<span class="hljs-string">''</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleEnterKey</span>(<span class="hljs-params"></span>) </span>{
  message.value = <span class="hljs-string">'Enter key pressed!'</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h4 id="heading-code-explanation-7">Code explanation:</h4>
<ul>
<li><code>@keydown.enter</code>: Listens for the <code>enter</code> keypress and triggers the <code>handleEnterKey</code> function when pressed. This is useful for form submissions or other actions that should be triggered by a specific key press.</li>
</ul>
<h3 id="heading-wrapping-up">Wrapping Up</h3>
<p>Event handling in Vue 3 is pretty straightforward and flexible. From basic click events to custom events and form handling, Vue's event system allows you to create interactive, dynamic applications.</p>
<p>By using event modifiers and custom events, you can fine-tune how events are handled in your app. Understanding these techniques will enable you to create responsive, user-friendly interfaces.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Stunning Animations with the Vue.js Transition API ]]>
                </title>
                <description>
                    <![CDATA[ I’m easily gripped by animations and purposeful motion on the Web – so much so that I wrote a whole article on it.  I’m also a big fan of the Vue.js framework, and I’ve been building apps with it for three years. So it was a delightful surprise when ]]>
                </description>
                <link>https://www.freecodecamp.org/news/animations-with-vuejs-transition-api/</link>
                <guid isPermaLink="false">66bc54a3e35f27b35395073b</guid>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Felix Favour Chinemerem ]]>
                </dc:creator>
                <pubDate>Tue, 10 Oct 2023 15:28:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/supercharged-animations.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I’m easily gripped by animations and purposeful motion on the Web – so much so that I wrote a <a target="_blank" href="https://favourfelix.com/stories/css-and-motion-build-animations-on-the-web/">whole article</a> on it. </p>
<p>I’m also a big fan of the Vue.js framework, and I’ve been building apps with it for three years.</p>
<p>So it was a delightful surprise when I realized that I could use <strong>only</strong> the Transition API in Vue.js while leveraging my decent CSS skills to animate a component’s enter and exit animations in such a fluid manner.</p>
<p>How fluid, you might ask? Let me show you:</p>
<div class="embed-wrapper">
        <iframe width="640" height="360" src="https://player.vimeo.com/video/872452747" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="Vimeo embed" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen="" loading="lazy"></iframe></div>
<p>In this article, we'll build a straightforward movie app with built-in filtering features. By the end, you should have a solid understanding of the <code>&lt;Transition&gt;</code> and <code>&lt;TransitionGroup&gt;</code> built-in components in Vue.js and how they seamlessly handle enter and exit animations within Vue.js.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Hold on! We need a few tools in our toolkit before we dive into this adventure. This article is designed to be a breeze for beginners, but to ensure a smooth ride, here's what you'll need:</p>
<ul>
<li>A basic understanding of HTML, CSS, and Javascript.</li>
<li>A <a target="_blank" href="https://www.freecodecamp.org/news/css-transition-vs-css-animation-handbook/">basic understanding of Transitions and Animations in CSS</a>.</li>
<li>A <a target="_blank" href="https://www.freecodecamp.org/news/vue-3-full-course/">basic knowledge of the Vue.js framework</a>.</li>
<li>You can also access the bare movie app without the transitions <a target="_blank" href="https://github.com/felixfavour/supercharged-animations-vue">here</a>, but only if you think it’s the boring part ;)</li>
<li>Last but not least, some good background music—I’ll let you choose this one yourself, <a target="_blank" href="https://coderadio.freecodecamp.org/">but there's always freeCodeCamp radio</a>.</li>
</ul>
<p>Ok, now we're ready to get started.</p>
<h2 id="heading-what-is-the-transition-api">What is the Transition API?</h2>
<p>The Transition API primarily consists of the built-in <code>&lt;Transition&gt;</code> and  <code>&lt;TransitionGroup&gt;</code> components in Vue.</p>
<p>The <code>&lt;Transition&gt;</code> component is used for animating single elements or components. In contrast, the <code>&lt;TransitionGroup&gt;</code> is used for animating multiple elements in a list in conjunction with the v-for directive in Vue.</p>
<h3 id="heading-how-to-add-animations-with-the-component">How to Add Animations with the <code>&lt;Transition&gt;</code> Component</h3>
<p>The <code>&lt;Transition&gt;</code> component is a built-in component that is typically wrapped around any root element or component for animation benefits. The animations are triggered when the inner element or component is shown or hidden using common Vue directives like v-show or v-if.</p>
<p>This component is "built-in" because it does not need to be imported into the template to be functional. It's recognized by the Vue template compiler.</p>
<p>We can try this out by adding a v-show to our <code>.header-filters</code> container first and wrapping the container in the <code>&lt;Transition&gt;</code> component like below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">Transition</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"filtersVisible"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header-filters"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"search"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"searchQuery"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search Movies"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button-group"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"query = ''"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{ active: query === '' }"</span>&gt;</span>Clear Filters<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"query = '2021'"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{ active: query === '2021' }"</span>&gt;</span>2021<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"query = 'Action'"</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">"{ active: query === 'Action' }"</span>&gt;</span>Action<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Transition</span>&gt;</span>
</code></pre>
<p>We can then wrap up the animation by including styling specifications for the enter and exit animations. If you are familiar with CSS Animations and Transitions, the mode of operation is pretty similar. If you are not, here is a <a target="_blank" href="https://favourfelix.com/stories/css-and-motion-build-animations-on-the-web/">quick crash course</a>.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.v-move</span>,
<span class="hljs-selector-class">.v-enter-active</span>,
<span class="hljs-selector-class">.v-leave-active</span> {
  <span class="hljs-attribute">transition</span>: <span class="hljs-number">0.3s</span> ease;
}

<span class="hljs-selector-class">.v-enter-from</span>,
<span class="hljs-selector-class">.v-leave-to</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateY</span>(<span class="hljs-number">10px</span>);
}
</code></pre>
<p>And now, you should have your fluid animations for single root elements with the <code>&lt;Transition&gt;</code> component. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ezgif.com-video-to-gif--1-.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Illustration of the effect of the Transition component when an element is hidden and shown.</em></p>
<p>What if you want something similar to the video that I shared earlier? Something more mesmerizing? Well, let’s see the <code>&lt;TransitionGroup&gt;</code> component.</p>
<h3 id="heading-how-to-add-list-animations-with-the-component">How to Add List Animations with the <code>&lt;TransitionGroup&gt;</code> Component</h3>
<p>The <code>&lt;TransitionGroup&gt;</code> component is wrapped around a list to animate the insertion, removal, and change of order of the items that are rendered in this list. This list is typically created with the v-for directive.</p>
<p>Unlike the <code>&lt;Transition&gt;</code> component, the elements or components wrapped in the <code>&lt;TransitionGroup&gt;</code> component must have unique key attributes.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">TransitionGroup</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">MovieCard</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"movie in filteredMovies"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"movie.title"</span> <span class="hljs-attr">:movie</span>=<span class="hljs-string">"movie"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">TransitionGroup</span>&gt;</span>
</code></pre>
<p>Aside from the instructions above, the <code>&lt;TransitionGroup&gt;</code> component has a similar mode of integration and operation as the <code>&lt;Transition&gt;</code> component.</p>
<h2 id="heading-how-to-identify-and-name-transitions">How to Identify and Name Transitions</h2>
<p>A common problem when using <code>&lt;Transition&gt;</code> and <code>&lt;TransitionGroup&gt;</code> components is having multiple instances of these components in your app and using different enter and exit transitions for specific instances. This is why we name <code>&lt;Transition&gt;</code> and <code>&lt;TransitionGroup&gt;</code> components.</p>
<p>The <code>&lt;Transition&gt;</code> and <code>&lt;TransitionGroup&gt;</code> components accept a <code>name</code> prop that helps to identify and group transitions.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">TransitionGroup</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"list"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">MovieCard</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"movie in filteredMovies"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"movie.title"</span> <span class="hljs-attr">:movie</span>=<span class="hljs-string">"movie"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">TransitionGroup</span>&gt;</span>
</code></pre>
<p>The <code>name</code> prop also determines the class name for styling enter and exit transitions like below:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.list-move</span>,
<span class="hljs-selector-class">.list-enter-active</span>,
<span class="hljs-selector-class">.list-leave-active</span> {
  <span class="hljs-attribute">transition</span>: <span class="hljs-number">0.3s</span> ease;
}

<span class="hljs-selector-class">.list-enter-from</span>,
<span class="hljs-selector-class">.list-leave-to</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateY</span>(<span class="hljs-number">10px</span>);
}
</code></pre>
<p>Notice that the <code>name</code> prop passed in the <code>&lt;TransitionGroup&gt;</code> code above is “list”, and it is used as a prefix in the styles for enter and exit transitions in the CSS code above.</p>
<h2 id="heading-how-to-customize-enter-and-exit-animations-with-css">How to Customize Enter and Exit Animations With CSS</h2>
<p>In this article, we used the CSS <code>transition</code> property to help with subtle enter and exit animations. But the <code>&lt;Transition&gt;</code> and <code>&lt;TransitionGroup&gt;</code> components also support the <code>animation</code> property in CSS for much more complex animations with multiple keyframes.</p>
<p>Here is an example of how we can use the <code>animation</code> property in our <code>&lt;Transition&gt;</code> component:</p>
<h3 id="heading-template">Template</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">Transition</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"bounce"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"filtersVisible"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header-filters"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"search"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"searchQuery"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search Movies"</span>&gt;</span>
    . . .
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Transition</span>&gt;</span>
</code></pre>
<h3 id="heading-styles">Styles</h3>
<pre><code class="lang-css"><span class="hljs-selector-class">.bounce-enter-active</span> {
  <span class="hljs-attribute">animation</span>: bounce-in <span class="hljs-number">0.5s</span>;
}
<span class="hljs-selector-class">.bounce-leave-active</span> {
  <span class="hljs-attribute">animation</span>: bounce-in <span class="hljs-number">0.5s</span> reverse;
}
<span class="hljs-keyword">@keyframes</span> bounce-in {
  0% {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">0</span>);
  }
  50% {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">1.25</span>);
  }
  100% {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">scale</span>(<span class="hljs-number">1</span>);
  }
}
</code></pre>
<p>If the concept of keyframes or the animation property in CSS does not make much sense to you, feel free to <a target="_blank" href="https://favourfelix.com/stories/css-and-motion-build-animations-on-the-web/">get a quick introduction to animations in CSS</a>.</p>
<h2 id="heading-purposeful-motion-on-the-web">Purposeful Motion on the Web</h2>
<p>In any discussion about web animations and transitions, it's crucial to address the "why" behind them. Why should we include motion on the web, and is it genuinely indispensable?</p>
<p>Motion on the web serves a purpose that extends far beyond mere aesthetics. It's a powerful tool for conveying messages to your users. Whether it's to elevate your storytelling, provide user feedback, or captivate their attention, animations can play a pivotal role in enhancing your users' experience.</p>
<p>Gone are the days when animations were added merely for decorative purposes. In the modern web landscape, every keyframe should be thoughtfully designed with a clear purpose, and the user should always be at the heart of your design decisions.</p>
<p>That being said, make sure not to overuse animations – as too many moving parts can be distracting and actually detract from the user experience. It's all about balance.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Phew, that was an exciting ride! I hope you enjoyed it. This article shared a lot, and I'm excited to see how you use that information. I would love to know if you built something cool while reading.</p>
<p>Also feel free to look into the official Vue <a target="_blank" href="https://vuejs.org/guide/built-ins/transition.html">documentation</a> to see what else is possible with the Transition API.</p>
<p>Finally, have fun creating delightful experiences with animation, but always remember CSS animations are only useful to your users when they are purposeful. If you found this article helpful, feel free to connect on <a target="_blank" href="http://favourfelix.com/">favourfelix.com</a> to see what else I'm up to.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What I Learned Speaking at VueConf US 2022 – Tech Conference Guide ]]>
                </title>
                <description>
                    <![CDATA[ By Austin Gil This year I had the honor of speaking at VueConf US 2022 and I thought I would share my experience.  Part of this article will cover my personal process, part will look at VueConf specifically, and the rest will be about the conference ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-i-learned-speaking-at-vueconf-us/</link>
                <guid isPermaLink="false">66d45da4787a2a3b05af4380</guid>
                
                    <category>
                        <![CDATA[ community ]]>
                    </category>
                
                    <category>
                        <![CDATA[ community building ]]>
                    </category>
                
                    <category>
                        <![CDATA[ conference ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vue ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 18 Aug 2022 19:51:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/Vue-Blog-Cover-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Austin Gil</p>
<p>This year I had the honor of speaking at <a target="_blank" href="https://us.vuejs.org/">VueConf US 2022</a> and I thought I would share my experience. </p>
<p>Part of this article will cover my personal process, part will look at VueConf specifically, and the rest will be about the conference at large. I hope you enjoy it.</p>
<h2 id="heading-how-to-get-into-vueconf">How to Get into VueConf</h2>
<h3 id="heading-call-for-papers">Call For Papers</h3>
<p>Let's start at the beginning, as that's a good place to start. As soon as VueConf announced the Call for Papers (CFPs), I submitted my talks. I had submitted a few, and the one they selected was called, "<a target="_blank" href="https://www.vuemastery.com/conferences/vueconf-us-2022/maintainable-and-resilient-projects-through-internal-ui-libraries/">Maintainable &amp; Resilient Projects Through Internal UI Libraries</a>".</p>
<p>I submitted other talks that I thought had a better chance, but they selected this one, which turned out really well.</p>
<p>The submission process the conference uses is a Google Form. There's nothing wrong with that, but I've noticed that some other conferences use <a target="_blank" href="https://sessionize.com/">Sessionize</a> or <a target="_blank" href="https://www.papercall.io/">PaperCall</a> which I prefer because you can have an account with your details that pre-fills the forms. With the Google Form, I had to re-type a lot of the same data each time (my life is so hard).</p>
<p>It's not a huge deal, but I mention it in case you're a conference organizer. Those dedicated platforms are pretty sweet. :)</p>
<p>After several weeks, maybe a month, I got the email that I'd been selected. Yay! That gave me plenty of time to prepare. </p>
<p>If you're thinking about speaking at a conference, here's my advice:</p>
<ul>
<li>Submit several talks. You're not limited to just one.</li>
<li>Find out who the organizers are and connect with them. They don't only pick their friends, but it doesn't hurt if they recognize you.</li>
<li>Try giving your talks ahead of time at meetups. It'll get nerves out, works out kinks, and gets you some good feedback.</li>
<li>Make your talk stand out. Probably the most important thing is that you need to take time to create a title, pitch, and description that will get the attention of the organizers AND the audience. Communicate the value succinctly.</li>
<li><strong>Do it</strong>! It's a great way to connect with people and potential future employers.</li>
</ul>
<p>Also, shout out to <a target="_blank" href="https://twitter.com/wesbos">Wes Bos</a> and <a target="_blank" href="https://twitter.com/stolinski">Scott Tolinski</a> for giving some good tips on a recent <a target="_blank" href="https://syntax.fm/show/480/10-years-of-speaking-conferences">Syntax.fm episode</a>.</p>
<p>After being selected, there was the matter of convincing my boss that our company should sponsor my trip.</p>
<h3 id="heading-how-to-convince-your-boss">How to Convince Your Boss</h3>
<p>Some conferences will actually cover flights, accommodations, and even pay speakers. I think it's far more common to cover all the expenses yourself, minus the entry. This means either paying for everything out of pocket, or having your company sponsor your trip (heck yeah!).</p>
<p>Convincing your company to send you to a conference can be tricky because it has to be worth it for them. That's not always easy to prove. If you're presenting, it's better because that could provide an opportunity to promote the company or engage with your community.</p>
<p>Of course, this is going to vary greatly based on your company, their budget, the nature of your role, your team, and your workload. </p>
<p>As a developer advocate, it's a lot more typical for me to go to conferences now, but there's a greater expectation for me to speak, run a booth, host some sort of gathering, and create content (ahem).</p>
<p>Some companies are really good about conference policies, others not so much. One cool thing VueConf does that's really cool is provide folks with a <a target="_blank" href="https://paper.dropbox.com/doc/Convince-Your-Boss-VueConfUS-2022-9T1Rjt4t2POm2O7aKpQ3U">"convince your boss" template</a> that you can copy and fill out with your own details.</p>
<p>I didn't use the template, but they do cover some things that I recommend. In particular, doing the research ahead of time for how much it's going to cost (tickets, flight, accommodation, meals, other travel, and so on) is a good idea.</p>
<p>Something else that helps is arranging to create content or a presentation that you can bring back to your organization. That can help spread your knowledge around and get more value for your employer.</p>
<h3 id="heading-how-to-write-the-dang-thing">How to Write the Dang Thing</h3>
<p>Once you know for sure that you're going, you have to also prepare your talk. If you've never written a presentation before, they're a <strong>LOT</strong> of work.</p>
<p>I don't even want to tell you how much time went into this presentation in case my boss reads this post (only kind of joking). That's another unspoken cost of sending someone.</p>
<p>You have to come up with a main concept, write an outline, write the script, find funny GIFs, come up with a new but more relevant title and hope no one notices, start wondering why they even picked you because it's not that good of subject after all, consider calling out sick, <strong>IMPOSTER SYNDROME</strong>, decide you don't care what people think and do it anyway because even if you fail you can always be a goat farmer, practice the talk, realize it's way too long, cut out half of the work you've already done, practice again, halve it again...and this all has to happen the night before your talk because you procrastinated. </p>
<p>And even if you don't you'll still be fiddling with your slides up until 5 minutes before you have to give your talk.</p>
<p>It's exhausting (but worth it).</p>
<h3 id="heading-how-to-write-a-conference-talk">How to Write a Conference Talk</h3>
<p>If your curious about my process for writing a talk, I've been working on it for a while now, and have come up with this little system:</p>
<ol>
<li>Start the content as a blog post.</li>
<li>Create a high level outline.</li>
<li>Shift concepts around into an order that works.</li>
<li>Fill in the gaps for a rough draft.</li>
<li>Copy the content into a markdown file.</li>
<li>Use <a target="_blank" href="https://sli.dev/">Slidev</a> (awesome project by <a target="_blank" href="https://twitter.com/antfu7">Anthony Fu</a>).</li>
<li>Come up with a story arch to make the content a little more interesting.</li>
<li>Fill in text.</li>
<li>Fill in helper graphics (code, charts, img, gifs)</li>
<li>Create speaker notes.</li>
<li>Practice reading through.</li>
<li>Practice reading through with timer.</li>
<li>Practice speaking through.</li>
<li>Practice speaking through with timer (camera optional).</li>
<li>Practice speaking it to <a target="_blank" href="https://www.instagram.com/nuggetthemighty/">Nugget</a>.</li>
</ol>
<p>I like this approach because in the end, I have a nice presentation as well as a rough draft for a blog post I can continue with later on. And Slidev is great because I can keep one main theme and reuse it for most of my presentations.</p>
<h2 id="heading-what-vueconf-is-like">What VueConf is Like</h2>
<h3 id="heading-the-check-in-process">The Check-in Process</h3>
<p>I arrived on the first day to help <a target="_blank" href="https://twitter.com/_jessicasachs">Jessica Sachs</a> give her workshop, "Stress-free Testing for Vue 3" (she's great and you should take her workshop some time). </p>
<p>The check in process was smooth, and one unique thing they had was a nice color-coded sticker system to communicate peoples preferred greeting: wave, hand-shake/fist-bump, hug. So you could stick it to your name tag to let other folks know your preference.</p>
<p>There wasn't much discussion around COVID protocols, but it's worth mentioning in case any of you are concerned about it. There was around 10 of us wearing masks, which I think goes for most events. It's mostly up to individuals, so if you're still uncomfortable in large groups, it's probably good to stick to the virtual events. I did hear of someone testing positive.</p>
<h3 id="heading-the-people-of-vueconf">The People of VueConf</h3>
<p>Ok, on to some more fun stuff – the people. This is one of my favorite parts of events, you get to meet so many great folks. And Vue folks are among the best!</p>
<p>I was happy to see greater diversity among the attendees than in the past. Like most tech events, it was still mostly men from the same few majority groups, but the gap didn't feel as stark as past events.</p>
<p>It felt like a larger distribution of people across the different race, gender, age, and experience spectrums. It was also represented in the speakers. It's working, people – more colors for our kick-ass rainbow!</p>
<h3 id="heading-how-to-meet-people-at-a-conference">How to meet people at a conference:</h3>
<ul>
<li>Don't hang out with the same people. It's easy to fall into chatting with the same people you know, but it's fun to meet new people and you never know what you'll learn.</li>
<li>People want to meet you. Some people have a harder time initiating a conversation, but are still eager to participate. Keep an eye out for folks and invite them in.</li>
<li>Follow the Pac-Man rule. If you find yourself in a circle of people chatting, always leave a gap for someone to step into. So your circle should look more like a Pac-Man.</li>
<li>Unless you need to recharge, during meals, sit at tables with people you don't know and strike up a conversation. It's great to see the variety of folks around you.</li>
</ul>
<h3 id="heading-vendors-at-conferences">Vendors at Conferences</h3>
<p>I don't spend a lot of time at the vendor booths, but they are super important for supporting events, and it's a good place to see who's taking care of your community. For that reason, I always like to swing by and talk to each vendor at least a little bit.</p>
<p>One thing I would really love to see from vendors is less wasteful swag. There's always so much stuff they give away for free that I'm sure just ends up in waste bins.</p>
<p>Some swag is super effective and a great way to get a brand name out there, but less garbage would also be awesome :)</p>
<h2 id="heading-vueconf-content">VueConf Content</h2>
<p>I thought the talks were well-selected, but they definitely leaned very heavily towards testing. I didn't mind that because each testing talk brought it's own perspective, and it's an important subject.</p>
<p>Not every talk was specific to Vue, which I always appreciate. It's cool to see a broad range of talks, both in the topics represented and in their target experience level. There was also a good range of talks from broad concepts to very specific, personal experiences.</p>
<p>This year I got to experience a workshop as well (as an assistant). I really liked the experience because workshops give you much more time on a single topic to really dive deep and get your hands dirty. And there's way more opportunity for one-on-one time between attendees and instructors.</p>
<p><strong>HOT TIP</strong>: if you know someone putting on a workshop, ask them if they need any help. It got me into a workshop for free, got my friend some extra help, and it got the attendees more individual attention. Triple-win!</p>
<p>VueConf follows a single-track system which means every single talk happens in order, in the same room. This is different than other conferences that do the multi-track approach where there are multiple tracks running simultaneously, with a more dedicated focus.</p>
<p>They each have their pros and cons:</p>
<ul>
<li>Single-track: You know where to go for each talk. You never have to choose between two talks. There's plenty of opportunity to follow the "hallway track" and just chat with folks. You inherently get a little bit of everything.</li>
<li>Multi-track: There's usually way more content to choose from. There's more content for each specific topic. It's less likely to have time slots with nothing that interests you.</li>
</ul>
<p>I don't know if I have a preference, but it's something I thought about.</p>
<p>As mentioned above, one of the presentations was done by yours truly. It was a lot of fun. And despite the overwhelming feeling of impostor syndrome, I felt like I did pretty well. </p>
<p>There were a few points that I could have improved upon or just missed, but I don't think it showed on stage. Some folks even told me it was their favorite talk.</p>
<p>That was really nice...( ˘ ³˘)♥︎♥︎♥(°◡°♡)</p>
<p>It's published on <a target="_blank" href="https://www.vuemastery.com/conferences/vueconf-us-2022/maintainable-and-resilient-projects-through-internal-ui-libraries/">Vue Mastery</a> now. You can find it here: <a target="_blank" href="https://www.vuemastery.com/conferences/vueconf-us-2022/maintainable-and-resilient-projects-through-internal-ui-libraries/">https://www.vuemastery.com/conferences/vueconf-us-2022/maintainable-and-resilient-projects-through-internal-ui-libraries/</a></p>
<p>Would love to hear what you think.</p>
<p>One last little tip/trick I might start doing is to ask organizers if I can give my talk on the first day. I'd rather get it out of the way so I can actually sleep at night and really enjoy the rest of the conference.</p>
<h2 id="heading-in-person-conference-extracurriculars">In-Person Conference Extracurriculars</h2>
<p>In case you've never been to an in-person conference, you should know that post-conference events (parties) are going on probably every night. And it's great! Most of the time it's around grabbing drinks at a bar, but some conferences focus more on networking events.</p>
<p>At VueConf this year, I went to a few events.</p>
<ul>
<li>The first night, after the workshops, there wasn't much because it was technically still before the conference. It was a great chance to catch up with <a target="_blank" href="https://twitter.com/elevatebart">Bart Ledoux</a>.</li>
<li>The second night there was no <strong>official</strong> event because the organizers were hosting the speaker dinner. This consisted of drinks at the hotel pool beforehand, some swimming, and then walking over to a Thai restaurant with amazing Massaman curry. I also got <a target="_blank" href="https://twitter.com/hootlex">Alex Kyriakidis</a> his first Thai tea.</li>
<li>The last night of the conference was spent once again at the hotel pool for swim and drinks, then there was a hosted happy hour with some food and drinks. After everything, we found a karaoke bar for some songs and drinks (there was some drama finding a place, but in true VueConf fashion, we found it).</li>
</ul>
<p>In case it wasn't obvious, a lot of the external events revolve around drinks and/or bars. Which is fine for me because I don't mind a few drinks and can keep myself under control. However, I wish there were more events that focused less around alcohol. It would be more inclusive for folks that prefer not to drink.</p>
<p>Anyway, regardless of the venue/activity, I think it's worth going to as many extra events as you can. They're usually a lot of fun, and are a great way to connect with folks, both professionally and to make new friends. There's so much value there.</p>
<p>Two tips, though:</p>
<ol>
<li>Don't forget to bring your badge to after parties (learned that one the hard way).</li>
<li>Try not to get trashed. Some folks did, and it's a bad look for you and possibly your employer (learned that the easy way).</li>
</ol>
<h2 id="heading-in-closing">In Closing</h2>
<p>Conferences are great, and VueConf was no exception. If you haven't been to a conference, you should go. If you've been to a conference, you should go again. I find they are a good way to recharge my battery.</p>
<p>From this lineup, my favorite talk was probably "<a target="_blank" href="https://www.vuemastery.com/conferences/vueconf-us-2022/how-we-migrated-our-huge-app-to-vue3">How we migrated our HUGE app to Vue 3</a>" by <a target="_blank" href="https://twitter.com/_snoozbuster">Alex Van Liew</a>. It was very informative from hands-on experience, but it also wasn't so subjective that it wasn't useful. Alex did a great job putting it together and delivering it (and I think he said it was his first talk ever).</p>
<p>One last minute tip for the whole event. Be active on Twitter before, during, and after. Put the conference name in your Twitter name. Use whatever hashtag is associated. It makes it way easier to find other folks. </p>
<p>And if you're not already on Twitter, you should get on it because everyone uses Twitter (right <a target="_blank" href="https://twitter.com/Frankyfraank">Adam</a>?).</p>
<p>Thank you so much for reading. If you liked this article, please <a target="_blank" href="https://twitter.com/share?via=heyAustinGil">share it</a>. It's one of the best ways to support me. You can also <a target="_blank" href="https://austingil.com/newsletter/">sign up for my newsletter</a> or <a target="_blank" href="https://twitter.com/heyAustinGil">follow me on Twitter</a> if you want to know when new articles are published.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Code a Framework – the  First Lines of Vue.js ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific Have you ever wondered how frameworks are built? A couple of weeks ago I was writing an article and asked myself, what's the first line of code Evan You wrote to build Vue.js? Well, thanks to Git and Evan You pushing Vue's code to Gi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-code-a-framework-vuejs-example/</link>
                <guid isPermaLink="false">66d45ee7868774922c884fe0</guid>
                
                    <category>
                        <![CDATA[ framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 25 May 2022 21:38:19 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/05/pexels-hans-middendorp-9092855.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>Have you ever wondered how frameworks are built? A couple of weeks ago I was writing an article and asked myself, what's the first line of code Evan You wrote to build Vue.js?</p>
<p>Well, thanks to Git and Evan You pushing Vue's code to GitHub, I have been able to travel back in time, like Marty McFly with his DeLorean time machine. But I went nine years back, to 2013, and "watched" Evan writing his code. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/217316-Back_to_the_Future-DeLorean-time_travel-car-movies-smoke.jpg" alt="Delorean time machine" width="600" height="400" loading="lazy">
<em>Photo by wallup.net</em></p>
<h2 id="heading-whats-the-goal-of-this-article">What's the goal of this article?</h2>
<p>I am writing this article to show you what's behind a popular tool like Vuejs and what could be the starting point for building something like it. Specifically, we'll look at what was Evan's You starting point.</p>
<p>We will learn from Vue's creator by looking at the source code of his oldest commits. We'll examine what he wrote to make the first implementation of a Vue application and how he wrote the logic in plain JavaScript to make the mustache syntax work.</p>
<h3 id="heading-what-is-mustache-syntax">What is Mustache syntax?</h3>
<p>Well, if you are wondering what the hack is mustach syntax, let me explain. It is a basic form of data binding used by Vuejs to interpolate text inside a template. </p>
<p>From the vue documentation:</p>
<blockquote>
<p>Vue.js uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying Vue instance’s data. The most basic form of data binding is text interpolation using the “Mustache” syntax (double curly braces):</p>
</blockquote>
<p><code>&lt;span&gt;Message: {{ msg }}&lt;/span&gt;</code></p>
<blockquote>
<p>The mustache tag will be replaced with the value of the msg property on the corresponding data object. It will also be updated whenever the data object’s msg property changes.</p>
</blockquote>
<p>Ok, now that you know what that is, in the next section I'll answer your next question...</p>
<h2 id="heading-what-will-i-learn-after-reading-this-article">What will I learn after reading this article?</h2>
<p>Ok fair enough, you want to know why should you read this article and what will you learn from it.</p>
<p>If you are either a seasoned Vuejs developer or just at the beginning of your journey, you will learn how everything started for a popular tool like Vue. </p>
<p>You will also learn how to hunt for a specific feature of a framework, browse old GitHub commits, and understand how you can apply plain JavaScript knowledge to start building the first features of one of the most popular frameworks of our time. </p>
<p>In the next section we will start exploring the Vue.js repository. We will look at the fist and second commits to understand what files were created for the initial setup of the framework. </p>
<p>This will help us find the feature that we are looking for (mustache syntax) and figure out also how the first Vue application was made.</p>
<h2 id="heading-exploring-vues-oldest-commits">Exploring Vue's Oldest Commits</h2>
<p>Alright, let's get started. If you want to follow me on this time traveling journey, hit this <a target="_blank" href="https://github.com/vuejs/vue/commits/0.6.0?after=218557cdec830a629252f4a9e2643973dc1f1d2d+349&amp;branch=0.6.0&amp;qualified_name=refs%2Ftags%2F0.6.0">link</a>. There, you'll find the Vuejs repository tagged 0.6.0. We are interested in its first and second commits.</p>
<p>I have downloaded a copy of the source code locally, precisely the source code of the second commit. Let's browse the code.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/jDeze8rA7cA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-the-folder-structure">The Folder Structure</h2>
<p>The project structure in the second commit is fairly simple. Apart from a bunch of configuration files for jshing, grund, GitHub and the two JSON files, we can see three folders:</p>
<ul>
<li>test</li>
<li>src</li>
<li>explorations</li>
</ul>
<p>The last one is an addition Evan made. The exploration folder wasn't there on the first commit. And it is in there that the actual creation of Vue.js started to take place.</p>
<p>We will come back here later in the article, but before that let's look at the fist commit to find Evan's fist lines of code. Spolier: everything starts with a test.</p>
<h2 id="heading-the-first-vue-test-case">The First Vue Test Case</h2>
<p>The first lines of code are, I believe, those written in the test.js file. This is where Evan used the Mocha library to write Vue's first test case and set up the tests framework in the first commit named <a target="_blank" href="https://github.com/vuejs/vue/commits/0.6.0?after=218557cdec830a629252f4a9e2643973dc1f1d2d+349&amp;branch=0.6.0&amp;qualified_name=refs%2Ftags%2F0.6.0">initial setup</a>.</p>
<p>Why is this relevant? Well, we are not just hunting for a specific feature but we also want to understand what's the starting point for building a tool like Vuejs. </p>
<p>Do you start by writing the implementation? Or do you write a basic test case just to set everything up so that you can write proper tests when you have an idea of what you want to implement? </p>
<p>Well, below you have your answer!</p>
<p>Let's check the code of the test/test.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> Element = <span class="hljs-built_in">require</span>(<span class="hljs-string">'element'</span>)

describe(<span class="hljs-string">'Element'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">'should have a variable'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        assert.equal(Element, <span class="hljs-number">123</span>)
    })
})
</code></pre>
<p>On the first line, there is a <code>require</code> statement to import an element class that will be defined somewhere later on in the code. Imagine this as the ancestor of the Vue class.</p>
<p>Next, the Mocha <code>describe</code> function is set to provide a general context.
Within it, an <code>it</code> function is called to write the actual test case that checks if the imported <code>Element</code> class is equal to <code>123</code>, which it does using the method <code>assert.equal()</code>.</p>
<p>To run the tests we will have to install all dependencies <code>npm i</code> and run the Grunt tasks. But since most of the libraries used are deprecated we won't do that (and this is also not the goal of this article and video).</p>
<p>In the next section, we will explore the second commit aiming to reach our goals – find the first implementation of Vue.js, and understand how the mustache syntax works.</p>
<p>To do that, we need to look at the second commit's source code, which is the one I have downloaded and am exploring on VSCode (if you are following the video as well).</p>
<p>Here is the <a target="_blank" href="https://github.com/vuejs/vue/tree/871ed9126639c9128c18bb2f19e6afd42c0c5ad9">direct link</a>.</p>
<h2 id="heading-the-first-vue-application">The First Vue Application</h2>
<p>In Vue, everything we do is done inside a Vue application, which is bound to an istance of the Vue class. So, we first need to find the first implementation of this class and in there we will find the logic behind the mustache syntax.</p>
<p>Ok, we need to look inside the explorations folder – here is where the magic happens.</p>
<p>The main file is called <code>getset-revitis-style.html</code>, and here we can see all the logic of the first Vue application (which you can find in the body tag) and its first implementation (which you can find in the <code>script</code> tag). </p>
<p>I made a copy of the entire file and placed everything inside an index.html file so we can mess around with the code, add some console logs, and explore how it works. </p>
<p>Let's serve the file using <code>serve -s</code>. (To run this command you will need to install an npm package. Just type in the terminal <code>npm install -g serve</code>.)</p>
<p>In the body tag we can see the Vue app, in <code>div</code> with an <code>id</code> of <code>test</code>. Today we either define our app inside a root element with an id of <code>app</code> or <code>root</code>, but at the time it started with a test div.</p>
<pre><code class="lang-html">
 <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"test"</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{msg}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{msg}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{msg}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{what}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{hey}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the test <code>div</code> we can see the double mustache syntax. Cool! This is the first time it was used, but how does it work? </p>
<p>In the next section we will explore the first Vue class and look for the logic to make this <code>{{msg}}</code> work.</p>
<h2 id="heading-the-first-vue-instance">The First Vue Instance</h2>
<p>Ok, we found the firt usage of this syntax, but we are not done yet. We want to know how it works, remember? So, let's look in the script tag where we will find the logic of the first Vue class. </p>
<p>Evan created a class called <code>Element</code> – remember that we are nine years back in time, and ES6 isn't a thing until 2015.</p>
<p>The class declaration is written using <code>function Element () {}</code>. This is the ancestor class of what we know today as the Vue instance that we instantiate by doing <code>new Vue()</code>.</p>
<pre><code class="lang-js">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Element</span> (<span class="hljs-params">id, initData</span>) </span>{
  <span class="hljs-comment">// The first implementation is in here</span>
}
</code></pre>
<p>Next, the first Vue instance is created by instantiating the Element class:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> Element(<span class="hljs-string">'test'</span>, {
  <span class="hljs-attr">msg</span>: <span class="hljs-string">'hello'</span>
})
</code></pre>
<p>The class expects an <code>id</code> and an <code>initData</code>. These are passed to the instance as the <code>test</code> value and as an object <code>{}</code> with a property called <code>msg</code>. This is our first implementation of the options object.</p>
<p>Ok, we are getting there. Now that we know how the class was implemented and instantiated, let's look inside to find how the double mustache syntax was implemented.</p>
<h2 id="heading-how-the-mustache-syntax-works">How the Mustache Syntax Works</h2>
<p>Here we are. The next code block will show us the syntax secrets. This is what we have been aiming to understand, the article's goal. </p>
<p>After this you will be able to understand what's behind this syntax and even edit and replace it with your own.</p>
<p>You could do something like <code>[[msg]]</code> and maybe call it the double box syntax. 🤓 </p>
<p>The code below is used to make the double mustache syntax work. In between there is more code that is responsible for how data are bound. </p>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> bindingMark = <span class="hljs-string">'data-element-binding'</span> <span class="hljs-comment">// &lt;-- data binding mark </span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Element</span> (<span class="hljs-params">id, initData</span>) </span>{
<span class="hljs-comment">// The first implementation is in here</span>
  <span class="hljs-keyword">var</span> self = <span class="hljs-built_in">this</span>,
  el = self.el = <span class="hljs-built_in">document</span>.getElementById(id)
  <span class="hljs-comment">//console.log(self.el)</span>

  bindings = {} <span class="hljs-comment">// the internal copy</span>
  data = self.data = {} <span class="hljs-comment">// the external interface</span>
  content = el.innerHTML.replace(<span class="hljs-regexp">/\{\{(.*)\}\}/g</span>, markToken)

  el.innerHTML = content


  <span class="hljs-comment">// ....</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">markToken</span>(<span class="hljs-params">match, variable</span>) </span>{
    <span class="hljs-built_in">console</span>.log(match) <span class="hljs-comment">// &lt;-- LOG match = {{msg}}</span>
    <span class="hljs-built_in">console</span>.log(variable) <span class="hljs-comment">// &lt;-- LOG captured group as variable = msg</span>
    <span class="hljs-comment">//console.log(bindings)</span>
    bindings[variable] = {}
    <span class="hljs-comment">//console.log(bindings)</span>

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'&lt;span '</span> + bindingMark + <span class="hljs-string">'="'</span> + variable + <span class="hljs-string">'"&gt;&lt;/span&gt;'</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-string">'&lt;span '</span> + bindingMark + <span class="hljs-string">'="'</span> + variable + <span class="hljs-string">'"&gt;&lt;/span&gt;'</span>
  }

  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>I have added a couple console logs to figure out what's inside two key parameters (<code>match</code> and <code>variables</code>) and what the <code>markToken</code> method returns.</p>
<p>Inside the script tag the fist line is a variable <code>var bindingMark = 'data-element-binding'</code>. This variable will be used as a data attribute and will replace the contents of the curly brackets using the replace method with a regular expression <code>el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken)</code>. </p>
<p>Yes, you got it right – behind this syntax there is plain JavaScript and specifically one of the oldest methods built in the language.</p>
<p><code>string.replace()</code> is a string method that accepts two parameters:</p>
<ul>
<li>the regular expression</li>
<li>a callback function  </li>
</ul>
<p>Check the result of the regex using a site like  to see what matches the regex <code>\{\{(.*)\}\}</code>.</p>
<p>When the callback function <code>markToken</code> is called, we have access to the match and the captured group, respectively used as parameters called <code>match</code> and <code>variables</code>.</p>
<p>You can see these two parameter values using the console logs I added in the source code.</p>
<p>Inside the <code>markToken</code> method, the first line after the console logs is <code>bindings[variable] = {}</code>. This is an internal copy of the data that will be used later for the data binings feature of the framework. </p>
<p>For each match, it sets a new property in the <code>bindings</code> object as an empty object. For instance, if we have <code>{{msg}}</code> a new property called <code>binginds[msg] = {}</code> will be created.</p>
<p>Finally, the return statement builds a <code>span</code> element that uses the value of the <code>bindingMark</code> variable as a data attribute, <code>data-element-binding</code>
and assigns to it the <code>variable</code> parameter as property.</p>
<p>So, instead of <code>{{mess}}</code> the following string <code>'&lt;span ' + bindingMark + '="' + variable + '"&gt;&lt;/span&gt;'</code> is created. The result is the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">data-element-binding</span>=<span class="hljs-string">"msg"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<p>The Vue code is still at its early stages. The implementation of the mustache syntax works alongside data bingind that is not yet fully implemented at this point of the framework.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we discovered the first steps Evan You took to build Vue.js. This is a raw implementation of the framework, and we have seen only a small bit of its code. But it can help us figure out how one of the framework features worked. And hey, things always start small to grow over time.</p>
<p>Let me know if you liked this kind of content. Reach out to me if you want to know what's behind a different feature of Vuejs. </p>
<p>You can also consider subscribing to my <a target="_blank" href="https://www.youtube.com/channel/UCTuFYi0pTsR9tOaO4qjV_pQ">YouTube channel</a>.</p>
<p>And you can follow me on <a target="_blank" href="https://twitter.com/Fab_Sky_Walker">Twitter</a> and <a target="_blank" href="https://www.linkedin.com/in/fabio-pacifici-com/">Linkedin</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Full-Stack Single Page Application with Laravel 9, MySQL, Vue.js, Inertia, Jetstream and Docker ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker. Le... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-full-stack-single-page-application-with-laravel-mysql-vue-and-docker/</link>
                <guid isPermaLink="false">66d45ee3052ad259f07e4ad4</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MySQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 16 May 2022 13:55:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/02/EP__3__LiveCoding.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this tutorial, you will learn how to build a single page application. I'll take you through the process step by step, using cutting edge technologies like Laravel 9, Jetstream, Vuejs, Inertiajs, MySQL, Tailwind CSS, and Docker.</p>
<p>Let's get started.</p>
<h2 id="heading-what-you-need-to-follow-this-guide">What you need to follow this guide:</h2>
<p>To follow along you will need: </p>
<ul>
<li>a computer</li>
<li>to know how to install software </li>
<li>a basic understanding of HTML, CSS, JavaScript, and PHP </li>
<li>knowledge of at least one JavaScript framework and an understanding of the MVC design pattern.</li>
</ul>
<p>This guide is organized into 10 chapters and is based off a live coding series that I record. The live coding series is completely unscripted, so there will be bugs and gotchas there that you won't find in this guide.</p>
<p>You can find the complete playlist at the end of this article.</p>
<p>Everything here should just work, but if it doesn't feel free to ask for help by joining my community on Slack. There you can share code snippets and chat with me directly. </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-tech-are-we-using">What Tech Are We Using?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-machine">How to Setup Your Machine</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to build the app with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</a></li>
<li><a class="post-section-overview" href="#heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</a></li>
<li><a class="post-section-overview" href="#heading-how-to-submit-forms-with-files">How to Submit Forms with Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-store-data">How to Store Data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-operations">How to Update Operations</a></li>
<li><a class="post-section-overview" href="#heading-how-to-delete-a-resource">How to Delete a Resourse</a></li>
<li><a class="post-section-overview" href="#heading-wrapup-and-whats-next">Wrap up and what's next</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-tech-are-we-using">What Tech Are We Using?</h2>
<p>First, let's go over the different tools we'll be using in this project.</p>
<h3 id="heading-docker">Docker</h3>
<p>Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers. </p>
<p>To simplify this concept, Docker lets you package applications and dependencies in a container. </p>
<p>A containerized application allows you to have a flexible development environment so that you can run different applications without worrying about dependencies, their requirements, and conflicts between different versions. You can easily run applications that, for instance, require two different versions of PHP and MySQL. </p>
<p>Each team member can quickly reproduce the same environment of your application by simply running the same container's configuration.</p>
<p>If you want to learn more about Docker, its <a target="_blank" href="https://www.docker.com/">Documentation</a> is a great place to start.</p>
<p>Here's a <a target="_blank" href="https://www.freecodecamp.org/news/the-docker-handbook/">Handbook on Docker essentials</a>, as well, so you can practice your skills.</p>
<h3 id="heading-mysql">Mysql</h3>
<p>MySQL is an open-source relational database management system. You can use it to organize data into one or more tables with data that may be related to each other.</p>
<p>We need to store data somewhere and here is where MySQL comes into play.</p>
<p>Here are the <a target="_blank" href="https://www.mysql.com/">Docs</a> if you want to read up more. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/learn-to-use-the-mysql-database/">full free course on MySQL</a> if you want to dive deeper.</p>
<h3 id="heading-laravel">Laravel</h3>
<p>Laravel is a free, open-source PHP web framework that helps you develop web applications following the model–view–controller architectural pattern.</p>
<p>Laravel is an amazing PHP framework that you can use to create bespoke web applications.</p>
<p>Here's the Laravel <a target="_blank" href="https://laravel.com/">Documentation</a> for more info, and here's a <a target="_blank" href="https://www.freecodecamp.org/news/laravel-full-course/">full project-based course</a> to help you learn Laravel.</p>
<h3 id="heading-laravel-sail">Laravel Sail</h3>
<p>Laravel Sail is a lightweight command-line interface for interacting with Laravel's default Docker development environment. </p>
<p>Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience.</p>
<p>Usually, creating a development environment to build such applications means you have to install software, languages, and frameworks on your local machine – and that is time-consuming. Thanks to Docker and Laravel Sail we will be up and running in no time!</p>
<p><strong>Laravel Sail is supported on macOS, Linux, and Windows <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">via WSL2</a>.</strong></p>
<p>Here's the <a target="_blank" href="https://laravel.com/docs/9.x/sail">Documentation</a> if you want to read up on it.</p>
<h3 id="heading-laravel-jetstream">Laravel Jetstream</h3>
<p>When building web applications, you likely want to let users register and log in to use your app. That is why we will use Jetstream.</p>
<p>Laravel Jetstream is a beautifully designed application starter kit for Laravel and provides the perfect starting point for your next Laravel application.</p>
<p>It uses Laravel Fortify to implement all the back end authentication logic.
Here are the <a target="_blank" href="https://jetstream.laravel.com/2.x/introduction.html">Docs</a>.</p>
<h3 id="heading-vuejs">Vuejs</h3>
<p>Vue.js is an open-source model–view–ViewModel front end JavaScript framework for building user interfaces and single-page applications.</p>
<p>Vue is a fantastic framework that you can use as a stand-alone to build single-page applications, but you can also use it with Laravel to build something amazing.</p>
<p>Here's the Vue <a target="_blank" href="https://vuejs.org/">Documentation</a> if you want to read up. And here's a <a target="_blank" href="https://www.freecodecamp.org/news/vue-3-full-course/">great Vue course</a> to get you started.</p>
<h3 id="heading-inertia-js">Inertia JS</h3>
<p>Inertia is the glue between Laravel and Vuejs that we will use to build modern single-page applications using classic server-side routing.</p>
<p>You can learn more about it in the <a target="_blank" href="https://inertiajs.com/">Documentation here</a>.</p>
<h3 id="heading-tailwind">Tailwind</h3>
<p>Tailwind CSS is a utility-first CSS framework packed with classes like flex, pt-4, text-center, and rotate-90 that you can use to build any design, directly in your markup</p>
<p>We'll use it in this project to build our design. Here's a <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-tailwindcss/">quick guide to get you up and running</a> if you aren't familiar with Tailwind.</p>
<h2 id="heading-how-to-set-up-your-machine">How to Set Up Your Machine</h2>
<p>To follow along with my live coding (and this tutorial), you will need to install Docker desktop on your machine. If you are using Windows, you will also need to enable WSL in your system settings.</p>
<p>Visit the Docker <a target="_blank" href="https://www.docker.com/get-started">getting started page</a> to install Docker Desktop.</p>
<p>If you are on Windows, enable WSL2 by following the steps <a target="_blank" href="https://docs.microsoft.com/en-us/windows/wsl/about">here</a>.</p>
<p>If you have any trouble, feel free to reach out or join my community on Slack to get help.</p>
<h2 id="heading-laravel-installation-with-sail">Laravel Installation with Sail</h2>
<p>If you have successfully installed Docker Desktop on your machine, we can open the terminal and install Laravel 9. </p>
<p>Open a terminal window and browse to a folder where you want to keep your project. Then run the command below to download the latest Laravel files. The command will put all files inside a folder called my-example-app, which you can tweak as you like.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Download laravel</span>
curl -s <span class="hljs-string">"https://laravel.build/my-example-app"</span> | bash
<span class="hljs-comment"># Enter the laravel folder</span>
<span class="hljs-built_in">cd</span> my-example-app
</code></pre>
<h3 id="heading-deploy-laravel-on-docker-using-the-sail-up-command">Deploy Laravel on Docker using the <code>sail up</code> command</h3>
<p>With Docker Desktop up and running, the next step is to start Laravel sail to build all the containers required to run our application locally.</p>
<p>Run the following command from the folder where all Laravel files have been downloaded:</p>
<pre><code class="lang-bash">vendor/bin/sail up
</code></pre>
<p>It will take a minute. Then visit <a target="_blank" href="http://localhost">http://localhost</a> and you should see your Laravel application.</p>
<p>If you run <code>sail up</code> and you get the following error, it is likely that you need to update Docker Desktop:</p>
<pre><code class="lang-bash">ERROR: Service <span class="hljs-string">'laravel.test'</span> failed to build:
</code></pre>
<h2 id="heading-how-to-build-the-app-with-laravel-9-laravel-sail-jetstram-inertia-and-vue3">How to Build the App with Laravel 9, Laravel Sail, Jetstram, Inertia and Vue3</h2>
<p>In this section, we will define a basic roadmap, install Laravel 9 with Laravel Sail, Run sail, and build the containers. </p>
<p>I will also take you on a tour of Laravel Sail and the sail commands. </p>
<p>Then we will install Jetstream and scaffold Vue and Inertia files and have a look at the files and available features.</p>
<p>Next, we will populate our database and add the front end provided by Jetstream to register an account and log into a fresh Laravel application.</p>
<p>Finally, we will have a look at the Jetstream dashboard, and the Inertia/Vue Components and then start playing around.</p>
<p>Along the way, we'll disable the registration, enable the Jetstream user profile picture feature, and then add our first Inertia page where we'll render some data taken from the database.</p>
<p>Here's the live coding video if you want to follow along that way:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/c0ibec9dhZA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>And if you prefer following along in this written tutorial, here are all the steps.</p>
<p>Just a reminder – you should have Laravel installed with Sail and have Docker set up on your machine. You can follow the steps above to do so if you haven't already.</p>
<h3 id="heading-laravel-sail-overview-sail-commands">Laravel Sail Overview – Sail Commands</h3>
<p>With Laravel Sail installed, our usual Laravel commands have sligtly changed.</p>
<p>For instance, instead of running the Laravel artisan command using PHP like <code>php artisan</code>, we now have to use Sail, like so: <code>sail artisan</code>.</p>
<p>The <code>sail artisan</code> command will return a list of all available Laravel commands.</p>
<p>Usually, when we work with Laravel, we also have to run the <code>npm</code> and <code>composer</code> commands.</p>
<p>Again, we need to prefix our commands with <code>sail</code> to make them run inside the container.</p>
<p>Below you'll find a list of some commands you will likely have to run:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Interact with the database - run the migrations</span>
sail artisan migrate <span class="hljs-comment"># It was: php artisan migrate</span>
<span class="hljs-comment"># Use composer commands</span>
sail composer require &lt;packageName&gt; <span class="hljs-comment"># it was: composer require &lt;packageName&gt;</span>
<span class="hljs-comment"># Use npm commands</span>
sail npm run dev <span class="hljs-comment"># it was: npm run dev</span>
</code></pre>
<p>You can read more in the <a target="_blank" href="https://laravel.com/docs/9.x/sail#executing-sail-commands">Sail documentation</a>.</p>
<h3 id="heading-install-jetstream-and-scaffold-vue-and-inertia">Install Jetstream and Scaffold Vue and Inertia</h3>
<p>Let's now install the Laravel Jetstream authentication package and use the Inertia scaffolding with Vue3.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> my-example-app
sail composer require laravel/jetstream
</code></pre>
<p>Remember to prefix the composer command with <code>sail</code>.</p>
<p>The command above has added a new command to Laravel. Now we need to run it to install all the Jetstream components:</p>
<pre><code class="lang-bash">sail artisan jetstream:install inertia
</code></pre>
<p>Next we need to compile all static assets with npm:</p>
<pre><code class="lang-bash">sail npm install
sail npm run dev
</code></pre>
<p>Before we can actually see our application, we will need to run the database migrations so that the session table, required by Jetstream, is present.</p>
<pre><code class="lang-bash">sail artisan migrate
</code></pre>
<p>Done! Jetstream is now installed in our application. If you visit <code>http://localhost</code> in your browser you should see the Laravel application with two links at the top to register and log in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/welcome-page.png" alt="welcome-page" width="600" height="400" loading="lazy"></p>
<h3 id="heading-populate-the-database-and-create-a-user-account">Populate the Database and Create a User Account</h3>
<p>Before creating a new user, let's have a quick look at the database configuration that Laravel Sail has created for us in the <code>.env</code> file.</p>
<pre><code class="lang-env">DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=my-example-app
DB_USERNAME=sail
DB_PASSWORD=password
</code></pre>
<p>As you can see, Laravel Sail configures everything we need to access the database container that is running on Docker. The <code>DB_DATABASE</code> is the name of the database and it is the same as the project folder. This is why in the previous step we were able to run the <code>migrate</code> command without issues.</p>
<p>Since we already migrated all database tables, we can now use the Laravel built-in user factory to create a new user then use its details to log in our user dashboard.</p>
<p>Let's open artisan tinker to interact with our application.</p>
<pre><code class="lang-bash">sail artisan tinker
</code></pre>
<p>The command above will open a command line interface that we can use to interact with our application. Let's create a new user.</p>
<pre><code class="lang-php">User::factory()-&gt;create()
</code></pre>
<p>The command above will create a new user and save its data in our database. Then it will render the user data onto the screen. Make sure to copy the user email so we can use it later to log in. Then exit by typing <code>exit;</code>.</p>
<p>The default password for every user created with a factory is <code>password</code>.</p>
<p>Let's visit the login page and access our application dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/loginpage.png" alt="loginpage" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstream-dashboard">Jetstream Dashboard</h3>
<p>After login you are redirected to the Jetstream dashboard, which looks amazing by default. We can customize it as we like, but it is just a starting point.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/dashboard.png" alt="dashboard" width="600" height="400" loading="lazy"></p>
<h3 id="heading-jetstreamvue-components-and-inertia-overview">Jetstream/Vue Components and Inertia Overview</h3>
<p>The first thing you may notice after installing Jetstram is that there are a number of Vue components registered in our application. Not only that, also Inertia brings in Vue components. </p>
<p>To use Inertia, we need to get familiar with it when defining routes.</p>
<p>When we installed Jetstream, it created inside the <code>resources/js</code> directory a number of subfolders where all our Vue components live. There are not just simple components but also Pages components rendered by inertia as our Views.</p>
<p>The Jetstream inertia scaffolding created:</p>
<ul>
<li><code>resources/js/Jetstream</code> Here we have 27 components used by Jetstream, but we can use them in our application too if we want.</li>
<li><code>resources/js/Layouts</code> In this folder there is the layout component used by inertia to render the dashboard page</li>
<li><code>resources/js/Pages</code> This is where we will place all our Pages (views) components. You will find the Dashboard page as well as the Laravel Welcome page components here.</li>
</ul>
<p>The power of Inertia mostly comes from how it connects Vue and Laravel, letting us pass data (Database Models and more) as props to our Vue Pages components.</p>
<p>When you open the <code>routes/web.php</code> file you will notice that we no longer return a view but instead we use <code>Inertia</code> to render a Page component.</p>
<p>Let's examine the <code>/</code> homepage route that renders the Welcome component.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Welcome'</span>, [
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
        <span class="hljs-string">'laravelVersion'</span> =&gt; Application::VERSION,
        <span class="hljs-string">'phpVersion'</span> =&gt; PHP_VERSION,
    ]);
});
</code></pre>
<p>It looks like our usual Route definition, exept that in the closure we are returning an <code>\Inertia\Response</code> by calling the <code>render</code> method of the Inertia class <code>Inertia::render()</code>. </p>
<p>This method accepts two parameters. The first is a component name. Here we passed the <code>Welcome</code> Page component, while the second parameter is an associative array that will turn into a list of <code>props</code> to pass to the component. Here is where the magic happens.</p>
<p>Looking inside the Welcome component, you will notice that in its script section, we simply define four props matching with the keys of our associative array. Then inertia will do the rest.</p>
<pre><code class="lang-vue">&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },
        // 👇 Define the props 
        props: {
            canLogin: Boolean, 
            canRegister: Boolean,
            laravelVersion: String,
            phpVersion: String,
        }
    })
&lt;/script&gt;
</code></pre>
<p>We can then just call the props inside the template. If you look at the template section you will notice that <code>laravelVersion</code> and <code>phpVersion</code> are referenced in the code as you normally would do with props in Vuejs.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0"</span>&gt;</span>
  Laravel v{{ laravelVersion }} (PHP v{{ phpVersion }})
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The dashboard component is a little different. In fact it uses the Layout defined under <code>Layouts/AppLayout.vue</code> and uses the <code>Welcome</code> component to render the Dashboard page content, which is the same as the laravel Welcome page.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>
                Dashboard
            <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white overflow-hidden shadow-xl sm:rounded-lg"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">welcome</span> /&gt;</span> 
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>Inside the layout component you will notice the two inertia components <code>Head</code> and <code>Link</code>.</p>
<p>We can use the <code>Head</code> component to add head elements to our page, like meta tags, page title, and so on. The <code>Link</code> component is a wrapper aroud a standard anchor tag that incercepts click events and prevents full page reload as you can read in the Inertia documentation.</p>
<p><a target="_blank" href="https://inertiajs.com/links">Link Component</a>
<a target="_blank" href="https://inertiajs.com/title-and-meta#head-component">Head Component</a></p>
<h3 id="heading-disable-the-registration-feature">Disable the Registration Feature</h3>
<p>If you are following along, the next step I'll take is to disable one on the features Jetstream provides – register an account. </p>
<p>To do that, we can navigate to <code>config/fortify.php</code> and comment out line 135 <code>Features::registration()</code> from the features array.</p>
<pre><code class="lang-php"><span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">//Features::registration(),</span>
        Features::resetPasswords(),
        <span class="hljs-comment">// Features::emailVerification(),</span>
        Features::updateProfileInformation(),
        Features::updatePasswords(),
        Features::twoFactorAuthentication([
            <span class="hljs-string">'confirmPassword'</span> =&gt; <span class="hljs-literal">true</span>,
        ]),
    ],
</code></pre>
<p>If we visit the welcome page we will notice that the <code>register</code> link is gone. Also, the route is no longer listed when we run <code>sail artisan route:list</code>.</p>
<h3 id="heading-enable-jetstream-user-profile-picture">Enable Jetstream User Profile Picture</h3>
<p>Now let's try to enable the Jetstream feature called ProfilePhotos. As you can guess, this will allow the user to add a profile picture.</p>
<p>To do that we need to visit <code>config/jetstream.php</code> and uncomment line 59 <code>Features::profilePhoto</code>.</p>
<pre><code class="lang-php">    <span class="hljs-string">'features'</span> =&gt; [
        <span class="hljs-comment">// Features::termsAndPrivacyPolicy(),</span>
        Features::profilePhotos(), <span class="hljs-comment">// 👈</span>
        <span class="hljs-comment">// Features::api(),</span>
        <span class="hljs-comment">// Features::teams(['invitations' =&gt; true]),</span>
        Features::accountDeletion(),
    ],
</code></pre>
<p>If you log in you will see that in the user profile, a new section is available to upload a profile picture.</p>
<p>But before doing anything else we need to run <code>sail artisan storage:link</code> so that Laravel creates a symlink to the <code>storage/app/public</code> folder where we will save all user profile images.</p>
<p>Now try to visit the user profile and update the profile picture. If you get a 404 on the image this is because by default Laravel sail assumes we are using Laravel valet and sets the app URL like so <code>APP_URL=http://my-example-app.test</code> in the <code>.env</code> file. Let's change it and use localhost instead.</p>
<pre><code class="lang-env">APP_URL=http://localhost
</code></pre>
<p>Now we should be good to go and be able to see and change our profile image!🥳</p>
<h3 id="heading-how-to-add-our-first-inertia-page-and-render-records-from-the-db">How to Add our First Inertia Page and Render Records from the DB</h3>
<p>Since we are rendering Vue components instead of blade views, it is wise to start <code>sail npm run watch</code> to watch and recompile our Vue components as we create or edit them. Next let's add a new Photos page.</p>
<p>I will start by creating a new Route inside web.php:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>);
});
</code></pre>
<p>In the code above I defined a new GET route and then rendered a component that I will place inside the <code>resources/js/Pages/Guest</code> and call <code>Photos</code>. Let's create it.</p>
<p>Create a Guest folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Guest
<span class="hljs-built_in">cd</span> Guest
touch Photos.vue
</code></pre>
<p>Then let's define a basic component:</p>
<pre><code class="lang-vue">&lt;template&gt;
  &lt;h1&gt;Photos Page&lt;/h1&gt;
&lt;/template&gt;
</code></pre>
<p>If we visit <code>http://localhost/photos/</code> we will see our new page, cool! Let's copy over the page structure from the Welcome page so that we get the login and dashboard links as well.</p>
<p>The component will change to this:</p>
<pre><code class="lang-vue">&lt;template&gt;
    &lt;Head title="Phots" /&gt;

    &lt;div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0"&gt;
        &lt;div v-if="canLogin" class="hidden fixed top-0 right-0 px-6 py-4 sm:block"&gt;
            &lt;Link v-if="$page.props.user" :href="route('admin.dashboard')" class="text-sm text-gray-700 underline"&gt;
                Dashboard
            &lt;/Link&gt;

            &lt;template v-else&gt;
                &lt;Link :href="route('login')" class="text-sm text-gray-700 underline"&gt;
                    Log in
                &lt;/Link&gt;

                &lt;Link v-if="canRegister" :href="route('register')" class="ml-4 text-sm text-gray-700 underline"&gt;
                    Register
                &lt;/Link&gt;
            &lt;/template&gt;
        &lt;/div&gt;

        &lt;div class="max-w-6xl mx-auto sm:px-6 lg:px-8"&gt;
            &lt;h1&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
    import { defineComponent } from 'vue'
    import { Head, Link } from '@inertiajs/inertia-vue3';

    export default defineComponent({
        components: {
            Head,
            Link,
        },

        props: {
            canLogin: Boolean,
            canRegister: Boolean,

        }
    })
&lt;/script&gt;
</code></pre>
<p>The next step is to render a bunch of data onto this new page. For that we will build a Model and add some records to the database.</p>
<pre><code class="lang-bash">saild artisan make:model Photo -mfcr
</code></pre>
<p>This command creates a Model called <code>Photo</code>, plus a database migration table class, a factory, and a resource controller.</p>
<p>Now let's define the database table inside the migration we just creted. Visit the <code>database/migrations</code> folder and you should see a file with a name similar to this: <code>2022_02_13_215119_create_photos_table</code> (yours will be sligly different).</p>
<p>Inside the migration file we can define a basic table like the following:</p>
<pre><code class="lang-php"> <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span>(<span class="hljs-params"></span>)
    </span>{
        Schema::create(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Blueprint $table</span>) </span>{
            $table-&gt;id();
            $table-&gt;string(<span class="hljs-string">'path'</span>);
            $table-&gt;text(<span class="hljs-string">'description'</span>);
            $table-&gt;timestamps();
        });
    }
</code></pre>
<p>For our table we defined just two new columns, <code>path</code> and <code>description</code>, plus the <code>id</code>, <code>created_at</code> and <code>updated_at</code> that will be created by the <code>$table-&gt;id()</code> and by the <code>$table-&gt;timestamps()</code> methods.</p>
<p>After the migration we will define a seeder and then run the migrations and seed the database.</p>
<p>At the top of the <code>database/seeders/PhotoSeeder.php</code> file we will import our Model and Faker:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">Photo</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Faker</span>\<span class="hljs-title">Generator</span> <span class="hljs-title">as</span> <span class="hljs-title">Faker</span>;
</code></pre>
<p>Next we will implement the run method using a for loop to create 10 records in the database.</p>
<pre><code class="lang-php">

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params">Faker $faker</span>)
    </span>{
        <span class="hljs-keyword">for</span> ($i = <span class="hljs-number">0</span>; $i &lt; <span class="hljs-number">10</span>; $i++) {
            $photo = <span class="hljs-keyword">new</span> Photo();
            $photo-&gt;path = $faker-&gt;imageUrl();
            $photo-&gt;description = $faker-&gt;paragraphs(<span class="hljs-number">2</span>, <span class="hljs-literal">true</span>);
            $photo-&gt;save();
        }
    }
</code></pre>
<p>We are ready to run the migrations and seed the database.</p>
<pre><code class="lang-php">
sail artisan migrate
sail artisan db:seed --<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PhotoSeeder</span></span>
</code></pre>
<p>We are now ready to show the data on the <code>Guest/Photos</code> page component.
First update the route and pass a collection of Photos as props to the rendered component:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//dd(Photo::all());</span>
    <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Guest/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all(), <span class="hljs-comment">## 👈 Pass a collection of photos, the key will become our prop in the component</span>
        <span class="hljs-string">'canLogin'</span> =&gt; Route::has(<span class="hljs-string">'login'</span>),
        <span class="hljs-string">'canRegister'</span> =&gt; Route::has(<span class="hljs-string">'register'</span>),
    ]);
});
</code></pre>
<p>Second, pass the prop to the props in the script section of the Guest/Photos component:</p>
<pre><code class="lang-js">
<span class="hljs-attr">props</span>: {
    <span class="hljs-attr">canLogin</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">canRegister</span>: <span class="hljs-built_in">Boolean</span>,
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span> <span class="hljs-comment">// 👈 Here</span>
}
</code></pre>
<p>Finally loop over the array and render all photos in the template section, just under the h1:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photos"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span> &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Done! if you visit the <code>/photos</code> page you should see ten photos. 🥳</p>
<h2 id="heading-how-to-refactor-the-admin-dashboard-and-create-new-admin-pages">How to Refactor the Admin Dashboard and Create New Admin Pages</h2>
<p>In this chapter we will Re-route the Jetstream dashboard and make a route group for all admin pages. </p>
<p>Then we will see how to add a new link to the dashboard and add a new admin page. </p>
<p>Finally we will take a collection of data from the db and render them in a basic table. The default table isn't cool enough, so for those reading this article, I decided to add a Tailwind table component.</p>
<h3 id="heading-re-route-the-jetstream-dashboard">Re-route the Jetstream Dashboard</h3>
<p>If we look at the <code>config/fortify.php</code> file we can see that around line 64 there is a key called home. It is calling the <code>Home</code> constant of the Route service provider.</p>
<p>This means that we can tweek the constant and redirect the authenticated user to a different route.</p>
<p>Lets go through it step-by-step:</p>
<ul>
<li>update the HOME Constant</li>
<li>make a route group and redirect logged in users to <code>admin/</code> instead of '/dashboard'</li>
</ul>
<p>Our application will have only a single user, so once they're logged in it is clearly the site admin – so makes sense to redirect to an <code>admin</code> URI.</p>
<p>Change the HOME constant in <code>app/Providers/RouteServiceProvider.php</code> around line 20 to match the following:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> HOME = <span class="hljs-string">'/admin'</span>;
</code></pre>
<h3 id="heading-how-to-add-an-admin-pages-route-group">How to Add an Admin Pages Route Group</h3>
<p>Next let's update our route inside web.php. We will change the route registered by Jetstream from this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);
</code></pre>
<p>To this:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// other admin routes here</span>
});
</code></pre>
<p>The route above is a route group that uses the <code>auth:sanctum</code> middleware for all routes within the group, a prefix of <code>admin</code>, and adds a <code>admin</code> suffix to each route name.</p>
<p>The end result is that we will be able to refer to the dashboard route by name, which now will be <code>admin.dashboard</code>. When we log in, we will be redirected to the <code>admin</code> route. Our dashboard route will respond since it's URI is just <code>/</code> but the goup prefix will prefix every route in the group and make their URI start with <code>admin</code>.</p>
<p>If you now run <code>sail artisan route:list</code> you will notice that the dashboard route has changed as we expected.</p>
<p>Before moving to the next step we need to update both the <code>/layouts/AppLayout.vue</code> and <code>/Pages/Welcome.vue</code> components.</p>
<p>Do you remeber that the dashboard route name is now <code>admin.dashboard</code> and not just <code>dashboard</code>?</p>
<p>Let's inspect the two components and update every reference of <code>route('dahsboard')</code> to this:</p>
<pre><code class="lang-js">route(<span class="hljs-string">'admin.dahsboard'</span>)
</code></pre>
<p>and also every reference of <code>route().current('dashboard')</code> to this:</p>
<pre><code class="lang-js">route().current(<span class="hljs-string">'admin.dashboard'</span>)
</code></pre>
<p>After all the changes, make sure to recompile the Vue components and watch changes by running <code>sail npm run watch</code>. Then visit the home page to check if everything is working.</p>
<h3 id="heading-how-to-add-a-new-link-to-the-dashboard">How to Add a New Link to the Dashboard</h3>
<p>Now, to add a new admin page where we can list all photos stored in the database, we need to add a new route to the group we created earlier. Let's hit the <code>web.php</code> file and make our changes.</p>
<p>In the Route group we will add a new route:</p>
<pre><code class="lang-php">Route::middleware([<span class="hljs-string">'auth:sanctum'</span>, <span class="hljs-string">'verified'</span>])-&gt;prefix(<span class="hljs-string">'admin'</span>)-&gt;name(<span class="hljs-string">'admin.'</span>)-&gt;group(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{

    Route::get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> Inertia::render(<span class="hljs-string">'Dashboard'</span>);
    })-&gt;name(<span class="hljs-string">'dashboard'</span>);

    <span class="hljs-comment">// 👇 other admin routes here 👇</span>

    Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos'</span>); <span class="hljs-comment">// This will respond to requests for admin/photos and have a name of admin.photos</span>

});
</code></pre>
<p>In the new route above we used the <code>inertia()</code> helper function that does the same exact thing – returns an Inertia/Response and renders our Page component. We placed the component under an <code>Admin</code> folder inside <code>Pages</code> and we will call it <code>Photos.vue</code>.</p>
<p>Before we create the component, let's add a new link to the dashboard that points to our new route.</p>
<p>Inside <code>AppLayout.vue</code>, find the <code>Navigation Links</code> comment and copy/paste the <code>jet-nav-link</code> component that is actually displaing a link to the dashboard and make it point to our new route.</p>
<p>You will end up having something like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Navigation Links --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.dashboard')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.dashboard')"</span>&gt;</span>
        Dashboard
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- 👇 here it is our new link --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">jet-nav-link</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos')"</span> <span class="hljs-attr">:active</span>=<span class="hljs-string">"route().current('admin.photos')"</span>&gt;</span>
        Photos
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-nav-link</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our link above uses <code>route('admin.photos')</code> to point to the correct route in the admin group.</p>
<p>If you visit <code>localhost/dashboard</code> and open the inspector, you should see an error:</p>
<pre><code class="lang-js"><span class="hljs-built_in">Error</span>: Cannot find <span class="hljs-built_in">module</span> <span class="hljs-string">`./Photos.vue`</span>
</code></pre>
<p>It is fine – we haven't created the Photos page component yet. So let's do it now!</p>
<h3 id="heading-how-to-add-a-new-admin-page-component">How to Add a New Admin Page Component</h3>
<p>Make a file named <code>Photos.vue</code> inside the <code>Pages/Admin</code> folder. Below are the bash commands to create the folder and the file via terminal, but you can do the same using your IDE's graphical interface.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> resources/js/Pages
mkdir Admin
touch Admin/Photos.vue
</code></pre>
<p>To make this new page look like the Dashboard page, we will copy over its content. You should end up having something like this:</p>
<pre><code class="lang-vue">
&lt;template&gt;
  &lt;app-layout title="Dashboard"&gt; &lt;!-- 👈 if you want you can update the page title --&gt;
    &lt;template #header&gt;
      &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
    &lt;/template&gt;

    &lt;div class="py-12"&gt;
      &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
        &lt;div class="bg-white overflow-hidden shadow-xl sm:rounded-lg"&gt;
          &lt;!-- 👇  All photos for the Admin page down here --&gt;
          &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/app-layout&gt;
&lt;/template&gt;

&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },
});
&lt;/script&gt;
</code></pre>
<p>I removed a few pieces from the Dashboard template so make sure to double check the code above. The <code>welcome</code> component was removed from the template as it is not required in this page, and also its reference in the script section. The rest is identical.</p>
<p>Feel free to update the page title referenced as prop on the <code>&lt;app-layout title="Dashboard"&gt;</code>.</p>
<p>Now when you visit <code>localhost/admin</code> you can click on the Photos menu item and see our Photos page component content. It's not much for now, just an <code>h1</code>.</p>
<h3 id="heading-how-to-render-records-in-the-admin-page-as-a-table">How to Render Records in the Admin Page as a Table</h3>
<p>Now it's time to render the data onto a table. To make things work let's first add our markup and fake that we already have access to as an array of objects and loop over them inside our table. Than we will figure out how to make things work for real.</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table-auto w-full text-left"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Desciption<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Actions<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"60"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{photo.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>View - Edit - Delete<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<p>Ok, since we assumed that our component has access to a list of Photos, let's pass a new prop to the component from the Route.</p>
<p>Update the route in web.php and pass to the <code>inertia()</code> function a second argument that will be an associative array. It will have its keys passed as props to the Vue Page component. </p>
<p>In it we will call <code>Photo::all()</code> to have a collection to assign to a <code>photos</code> key, but you can use other eloquent methods if you want to paginate the results, for example.</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/Photos'</span>, [
        <span class="hljs-string">'photos'</span> =&gt; Photo::all()
    ]);
})-&gt;name(<span class="hljs-string">'photos'</span>);
</code></pre>
<p>To connect the prop to our Page component we need to define the prop also inside the component.</p>
<pre><code class="lang-js">&lt;script&gt;
<span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
  <span class="hljs-attr">components</span>: {
    AppLayout,
  },
  <span class="hljs-comment">/* 👇 Pass the photos array as a props 👇 */</span>
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
  },
});
&lt;/script&gt;
</code></pre>
<h4 id="heading-extra-how-to-use-a-tailwind-table-component">Extra: How to use a Tailwind table component</h4>
<p>Tailwind is a CSS framework similar to Bootstrap. There are a number of free to use components that we can grab from the documentation, tweak, and use.</p>
<p>This table component is free and looks nice:<a target="_blank" href="https://tailwindui.com/components/application-ui/lists/tables">https://tailwindui.com/components/application-ui/lists/tables</a>.</p>
<p>We can tweek the Photos page template and use the Tailwind table component to get a nice looking table like so:</p>
<pre><code class="lang-vue">
&lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
              &lt;!-- All posts goes here --&gt;
              &lt;h1 class="text-2xl"&gt;Photos&lt;/h1&gt;
              &lt;a class="px-4 bg-sky-900 text-white rounded-md" href&gt;Create&lt;/a&gt;
              &lt;div class="flex flex-col"&gt;
                  &lt;div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"&gt;
                      &lt;div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"&gt;
                          &lt;div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"&gt;
                              &lt;table class="min-w-full divide-y divide-gray-200"&gt;
                                  &lt;thead class="bg-gray-50"&gt;
                                      &lt;tr&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;ID&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Photos&lt;/th&gt;
                                          &lt;th
                                              scope="col"
                                              class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                                          &gt;Description&lt;/th&gt;
                                          &lt;th scope="col" class="relative px-6 py-3"&gt;
                                              &lt;span class="sr-only"&gt;Edit&lt;/span&gt;
                                          &lt;/th&gt;
                                      &lt;/tr&gt;
                                  &lt;/thead&gt;
                                  &lt;tbody class="bg-white divide-y divide-gray-200"&gt;
                                      &lt;tr v-for="photo in photos" :key="photo.id"&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div
                                                  class="text-sm text-gray-900"
                                              &gt;{{ photo.id }}&lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="flex items-center"&gt;
                                                  &lt;div class="flex-shrink-0 h-10 w-10"&gt;
                                                      &lt;img
                                                          class="h-10 w-10 rounded-full"
                                                          :src="photo.path"
                                                          alt
                                                      /&gt;
                                                  &lt;/div&gt;
                                              &lt;/div&gt;
                                          &lt;/td&gt;

                                          &lt;td class="px-6 py-4 whitespace-nowrap"&gt;
                                              &lt;div class="text-sm text-gray-900"&gt;
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              &lt;/div&gt;
                                          &lt;/td&gt;
                                        &lt;!-- ACTIONS --&gt;
                                          &lt;td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"&gt;
                                              &lt;a href="#" class="text-indigo-600 hover:text-indigo-900"&gt;
                                              View - Edit - Delete
                                              &lt;/a&gt;
                                          &lt;/td&gt;
                                      &lt;/tr&gt;
                                  &lt;/tbody&gt;
                              &lt;/table&gt;
                          &lt;/div&gt;
                      &lt;/div&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;
</code></pre>
<h2 id="heading-how-to-submit-forms-with-files">How to Submit Forms with Files</h2>
<p>For the next section we will look into how to submit a form so that we can add a new photo to the database.</p>
<ul>
<li>Add a create button</li>
<li>Add a create route</li>
<li>Define the PhotosCreate component</li>
<li>Add a form</li>
<li>Validate data</li>
<li>Show validation errors</li>
<li>Save the file to the filesystem</li>
<li>Save the model</li>
</ul>
<h3 id="heading-how-to-create-a-new-photo">How to Create a New Photo</h3>
<p>Add a link that points to a create route:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"route('admin.photos.create')"</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Create the route within the admin group:</p>
<pre><code class="lang-php">Route::get(<span class="hljs-string">'/photos/create'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosCreate'</span>);
})-&gt;name(<span class="hljs-string">'photos.create'</span>);
</code></pre>
<p>Let's add also the route that will handle the form submission for later:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    dd(<span class="hljs-string">'I will handle the form submission'</span>)   
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>Create the <code>Admin/PhotosCreate.vue</code> component:</p>
<pre><code class="lang-vue">
    &lt;template&gt;
    &lt;app-layout title="Dashboard"&gt;
        &lt;template #header&gt;
            &lt;h2 class="font-semibold text-xl text-gray-800 leading-tight"&gt;Photos&lt;/h2&gt;
        &lt;/template&gt;

         &lt;div class="py-12"&gt;
            &lt;div class="max-w-7xl mx-auto sm:px-6 lg:px-8"&gt;
                &lt;h1 class="text-2xl"&gt;Add a new Photo&lt;/h1&gt;
                &lt;!-- 👇 Photo creation form goes here --&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/app-layout&gt;
&lt;/template&gt;


&lt;script&gt;
import { defineComponent } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";

export default defineComponent({
  components: {
    AppLayout,
  },

});
&lt;/script&gt;
</code></pre>
<h2 id="heading-how-to-add-the-form-to-the-component">How to Add the Form to the Component</h2>
<p>The next step is to add the form to the page and figure out how to submit it.</p>
<p>If you hit the Inertia documentation you will find out that there is a useForm class that we can use to simplify the process.</p>
<p>First, import the module inside the script tag of the Admin/PhotosCreate.vue component:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
</code></pre>
<p>Next we can use it in the setup function (Vue 3 composition API):</p>
<pre><code class="lang-js">setup () {
    <span class="hljs-keyword">const</span> form = useForm({
      <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
    })

    <span class="hljs-keyword">return</span> { form }
  }
</code></pre>
<p>In the code above we defined the function called <code>setup()</code> then a constant called <code>form</code> to have the <code>useForm()</code> class assigned to it.</p>
<p>Inside its parentheses we defined two properties, <code>path</code> and <code>description</code> which are the column names of our photos model.</p>
<p>Finally we returned the <code>form</code> variable for the setup function. This is to make the variable available inside our template.</p>
<p>Next we can add the form markup:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.store'))"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Description <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>&gt;</span>{{form.errors.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span> Photo <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span> <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span> @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{form.errors.path}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>&gt;</span>Save<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>The code above uses the Vue v-on directive short end syntax <code>@submit.prevent="form.post(route('admin.photos.store'))"</code> on the form tag, and the dom event <code>submit</code> with the <code>prevent</code> modifier. </p>
<p>Then it uses the <code>form</code> variable that we created earlier and a <code>post</code> method. This is available because we are using the <code>useForm</code> class. </p>
<p>Next we point the form to the route named admin.photos.store that we created earlier.</p>
<p>Inside the form we have two groups of inputs. First, we have the textarea that uses the v-model to bind it to the property <code>form.description</code> that we declared before.</p>
<p>The second group uses the <code>form.path</code> in a Tailwind component (showing the markup for a drop file area).</p>
<p>Right now we are allowing users to upload only a single photo using the v-on directive on the input DOM event <code>@input="form.path = $event.target.files[0]"</code>.</p>
<p>The last two things to notice are the error handling done via <code>&lt;div class="text-red-500" v-if="form.errors.path"&gt;{{form.errors.path}}&lt;/div&gt;</code> for the path and also for the description.</p>
<p>Finally we use <code>form.processing</code> to disable the submit button while the form is processing.</p>
<p>The next step is to define the logic to save the data inside the database.</p>
<h2 id="heading-how-to-store-data">How to Store Data</h2>
<p>To store the data, we can edit the route we defined earlier like so:</p>
<pre><code class="lang-php">Route::post(<span class="hljs-string">'/photos'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request</span>) </span>{
    <span class="hljs-comment">//dd('I will handle the form submission')  </span>

    <span class="hljs-comment">//dd(Request::all());</span>
    $validated_data = $request-&gt;validate([
        <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:2500'</span>],
        <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
    ]);
    <span class="hljs-comment">//dd($validated_data);</span>
    $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
    $validated_data[<span class="hljs-string">'path'</span>] = $path;
    <span class="hljs-comment">//dd($validated_data);</span>
    Photo::create($validated_data);
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.store'</span>);
</code></pre>
<p>The code above uses dependency injection to allow us to use the parameter <code>$request</code> inside the callback function. </p>
<p>We first validate the request and save the resulting array inside the variable <code>$validated_data</code>. Then we use the <code>Storage</code> facades to save the file in the filesystem and obtain the file path that we store inside the <code>$path variable</code>.</p>
<p>Finally we add a <code>path</code> key to the associative array and pass to it the <code>$path</code> variable. Next we create the resource in the database using the <code>Photo::create</code> method and redirect the user to the <code>admin.photos</code> page using the new <code>to_route()</code> helper function.</p>
<p>Make sure to import the <code>Request</code> class and the <code>Storage</code> facades at the top of the web.php file like so:</p>
<pre><code class="lang-php"><span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Request</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Storage</span>;
</code></pre>
<p>Now we can add a new photo in the database and show a list of photos for both the admin and standard visitors.</p>
<p>Next we need to complete the CRUD operations and allow the user to edit/update a photo and delete it.</p>
<h2 id="heading-how-to-update-operations">How to Update Operations</h2>
<p>Let's start by adding the routes responsible for showing the forms used to edit the resource and update its values onto the database.</p>
<p>Just under the other routes in the Admin group, let's add the following code:</p>
<pre><code class="lang-php">
Route::get(<span class="hljs-string">'/photos/{photo}/edit'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">Photo $photo</span>)</span>{
     <span class="hljs-keyword">return</span> inertia(<span class="hljs-string">'Admin/PhotosEdit'</span>, [
            <span class="hljs-string">'photo'</span> =&gt; $photo
        ]);
})-&gt;name(<span class="hljs-string">'photos.edit'</span>);
</code></pre>
<p>The route above uses dependency injection to inject inside the function the current post, selected by the URI <code>/photos/{photo}/edit</code>. </p>
<p>Next it returns the Inertia response via the <code>inertia()</code> function that accepts the Component name <code>'Admin/PhotosEdit'</code> as its first parameter and an associative array as its second.</p>
<p>Doing <code>['photo' =&gt; $photo]</code> will allow us to pass the <code>$photo</code> model as a prop to the component later.</p>
<p>Next let's add the new Page component under <code>resources/js/Pages/Admin/PhotosEdit.vue</code></p>
<p>This will be its template:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Edit Photo"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Edit Photo<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"form.post(route('admin.photos.update', photo.id))"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                            <span class="hljs-attr">for</span>=<span class="hljs-string">"description"</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>
                        &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                                <span class="hljs-attr">id</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span>
                                <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md"</span>
                                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"lorem ipsum"</span>
                                <span class="hljs-attr">v-model</span>=<span class="hljs-string">"form.description"</span>
                            /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-2 text-sm text-gray-500"</span>&gt;</span>Brief description for your photo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>
                            <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.description"</span>
                        &gt;</span>{{ form.errors.description }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-2"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700"</span>&gt;</span>Photo<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"</span>
                            &gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space-y-1 text-center"</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                                        <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-auto h-12 w-12 text-gray-400"</span>
                                        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                                        <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
                                        <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 48 48"</span>
                                        <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span>
                                    &gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                                            <span class="hljs-attr">d</span>=<span class="hljs-string">"M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"</span>
                                            <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
                                            <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
                                            <span class="hljs-attr">stroke-linejoin</span>=<span class="hljs-string">"round"</span>
                                        /&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex text-sm text-gray-600"</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                                            <span class="hljs-attr">for</span>=<span class="hljs-string">"path"</span>
                                            <span class="hljs-attr">class</span>=<span class="hljs-string">"relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"</span>
                                        &gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Upload a file<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                                                <span class="hljs-attr">id</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">name</span>=<span class="hljs-string">"path"</span>
                                                <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span>
                                                <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>
                                                @<span class="hljs-attr">input</span>=<span class="hljs-string">"form.path = $event.target.files[0]"</span>
                                            /&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pl-1"</span>&gt;</span>or drag and drop<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>PNG, JPG, GIF up to 10MB<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"form.errors.path"</span>&gt;</span>{{ form.errors.path }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                        <span class="hljs-attr">:disabled</span>=<span class="hljs-string">"form.processing"</span>
                        <span class="hljs-attr">class</span>=<span class="hljs-string">"inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>
                    &gt;</span>Update<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The template is actually identical to the Create component, except for a few things. The form points to a route that expects a paramenter that we pass as the second argument to the funtion <code>route</code>. It looks like this: <code>&lt;form @submit.prevent="form.post(route('admin.photos.update', photo.id))"&gt;</code>.</p>
<p>There is a section where we can see the original photo next to the upload form group:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"preview p-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"'/storage/' + photo.path"</span> <span class="hljs-attr">alt</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The rest is identical, and here we have the script section:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photo</span>: <span class="hljs-built_in">Object</span>
    },
    setup(props) {
        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"PUT"</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: props.photo.description,
        })

        <span class="hljs-keyword">return</span> { form }
    },

});
</code></pre>
<p>Notice that we are passing a props object with the photo key, which allows us to reference the model in the template.</p>
<p>Next, this <code>_method: "PUT",</code> line of code is required to be able to submit a <code>PUT</code> request instead of the <code>POST</code> request called on the form tag.</p>
<p>Now let's implement the logic to handle the form submission inside the Route below.</p>
<p>In web.php just under the previous route, let's add one that responds to the PUT request submitted by our form.</p>
<pre><code class="lang-php">Route::put(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Request $request, Photo $photo</span>)
    </span>{
        <span class="hljs-comment">//dd(Request::all());</span>

        $validated_data = $request-&gt;validate([
            <span class="hljs-string">'description'</span> =&gt; [<span class="hljs-string">'required'</span>]
        ]);

        <span class="hljs-keyword">if</span> ($request-&gt;hasFile(<span class="hljs-string">'path'</span>)) {
            $validated_data[<span class="hljs-string">'path'</span>] = $request-&gt;validate([
                <span class="hljs-string">'path'</span> =&gt; [<span class="hljs-string">'required'</span>, <span class="hljs-string">'image'</span>, <span class="hljs-string">'max:1500'</span>],

            ]);

            <span class="hljs-comment">// Grab the old image and delete it</span>
            <span class="hljs-comment">// dd($validated_data, $photo-&gt;path);</span>
            $oldImage = $photo-&gt;path;
            Storage::delete($oldImage);

            $path = Storage::disk(<span class="hljs-string">'public'</span>)-&gt;put(<span class="hljs-string">'photos'</span>, $request-&gt;file(<span class="hljs-string">'path'</span>));
            $validated_data[<span class="hljs-string">'path'</span>] = $path;
        }

        <span class="hljs-comment">//dd($validated_data);</span>

        $photo-&gt;update($validated_data);
        <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
    })-&gt;name(<span class="hljs-string">'photos.update'</span>);
</code></pre>
<p>The route logic is straigthforward. First we validate the description, next we check if a file was uploaded and if so we validate it. </p>
<p>Then we delete the previously uploaded image <code>Storage::delete($oldImage);</code> before storing the new image onto the datadabse and update the resource using <code>$photo-&gt;update($validated_data);</code>.</p>
<p>As before with the store route, we redirect to the <code>admin.photos</code> route using <code>return to_route('admin.photos');</code>.</p>
<h2 id="heading-how-to-delete-a-resource">How to Delete a Resource</h2>
<p>The last step we need to take is to write the logic to delete the photo. Let's start by adding the route.</p>
<p>Right below the previous route we can write:</p>
<pre><code class="lang-php">Route::delete(<span class="hljs-string">'/photos/{photo}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">Photo $photo</span>)
</span>{
    Storage::delete($photo-&gt;path);
    $photo-&gt;delete();
    <span class="hljs-keyword">return</span> to_route(<span class="hljs-string">'admin.photos'</span>);
})-&gt;name(<span class="hljs-string">'photos.delete'</span>);
</code></pre>
<p>This route is also using a wildcard in its URI to identify the resource. Next, its second paramenter is the callback that uses the dependency injection as before. Inside the callback we first delete the image from the filesystem using <code>Storage::delete($photo-&gt;path);</code>.</p>
<p>Then we remove the resource from the database <code>$photo-&gt;delete();</code> and redirect the user back <code>return to_route('admin.photos');</code> like we did in the previous reoute.</p>
<p>Now we need to add a delete button to the table we created in one of the previous steps to show all photos.</p>
<p>Inside the template section of the component <code>Admin/Photos.vue</code> within the <code>v-for</code>, we can add this Jetstream button:</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
    Delete
<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
</code></pre>
<p>Find the table cell that has the <code>ACTIONS</code> comment and replace the <code>DELETE</code> text with the button above.</p>
<p>So the final code will be:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<p>As you can see there is a <code>@click</code> event listener on the button. It calls a method <code>delete_photo(photo)</code> that we need to define along with a bunch of other methods to have a nice modal opening to ask for confirmation from the user.</p>
<p>First import the Inertia helper function useForm:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 0. Import the useForm class at the top of the script section along with all required components</span>
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
</code></pre>
<p>Remember to register the component <code>JetDangerButton</code> inside the components object before moving forward.</p>
<p>Next add the <code>setup()</code> function in the script section and implement the logic required to submit the form and show a modal. The comments in the code will guide you thorought all the steps.</p>
<pre><code class="lang-js"><span class="hljs-comment">// 1. add the setup function</span>
setup() {
    <span class="hljs-comment">// 2. declare a form variable and assign to it the Inertia useForm() helper function </span>
    <span class="hljs-keyword">const</span> form = useForm({
        <span class="hljs-comment">// 3. override the form method to make a DELETE request</span>
        <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
    });
    <span class="hljs-comment">// 4. define a reactive object with show_modal and photo property</span>
    <span class="hljs-comment">// this will be used to figure out when to show the modal and the selected post values</span>
    <span class="hljs-keyword">const</span> data = ref({
        <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">photo</span>: {
            <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
        }
    })

    <span class="hljs-comment">// 5. define the delete_photo function and update the values of the show_modal and photo properties</span>
    <span class="hljs-comment">// of the reactive object defined above. This method is called by the delete button and will record the details </span>
    <span class="hljs-comment">// of the selected post</span>
    <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
        <span class="hljs-comment">//console.log(photo);</span>
        <span class="hljs-comment">//console.log(photo.id, photo.path, photo.description);</span>
        data.value = {
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: photo.id,
                <span class="hljs-attr">path</span>: photo.path,
                <span class="hljs-attr">description</span>: photo.description
            },
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
        };
    }
    <span class="hljs-comment">// 6. define the method that will be called when our delete form is submitted</span>
    <span class="hljs-comment">// the form will be created next</span>
    <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
        form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
        closeModal();
    }
    <span class="hljs-comment">// 7. delare a method to close the modal by setting the show_modal to false</span>
    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
        data.value.show_modal = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-comment">// 8. remember to return from the setup function the all variables and methods that you want to expose </span>
    <span class="hljs-comment">// to the template.</span>
    <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

}
</code></pre>
<p>Finally outside the <code>v-for</code> loop add the modal using the following code. You can place this where you want but not inside the loop.</p>
<pre><code class="lang-html">
 <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
        Photo {{ data.photo.description.slice(0, 20) + '...' }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
        Are you sure you want to delete this photo?

    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
</code></pre>
<p>This is our final JavaScript code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { defineComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> AppLayout <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Layouts/AppLayout.vue"</span>;
<span class="hljs-keyword">import</span> TableComponent <span class="hljs-keyword">from</span> <span class="hljs-string">"@/Components/TableComponent.vue"</span>;
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/inertia-vue3'</span>;
<span class="hljs-keyword">import</span> JetDialogModal <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DialogModal.vue'</span>;
<span class="hljs-keyword">import</span> JetDangerButton <span class="hljs-keyword">from</span> <span class="hljs-string">'@/Jetstream/DangerButton.vue'</span>
<span class="hljs-keyword">import</span> { ref } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineComponent({
    <span class="hljs-attr">components</span>: {
        AppLayout,
        Link,
        TableComponent,
        JetDialogModal,
        JetDangerButton
    },
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">photos</span>: <span class="hljs-built_in">Array</span>,
    },

    setup() {

        <span class="hljs-keyword">const</span> form = useForm({
            <span class="hljs-attr">_method</span>: <span class="hljs-string">"DELETE"</span>,
        });
        <span class="hljs-keyword">const</span> data = ref({
            <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">photo</span>: {
                <span class="hljs-attr">id</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">path</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-literal">null</span>,
            }

        })


        <span class="hljs-keyword">const</span> delete_photo = <span class="hljs-function">(<span class="hljs-params">photo</span>) =&gt;</span> {
            <span class="hljs-comment">//console.log(photo);</span>
            <span class="hljs-built_in">console</span>.log(photo.id, photo.path, photo.description);
            data.value = {
                <span class="hljs-attr">photo</span>: {
                    <span class="hljs-attr">id</span>: photo.id,
                    <span class="hljs-attr">path</span>: photo.path,
                    <span class="hljs-attr">description</span>: photo.description
                },
                <span class="hljs-attr">show_modal</span>: <span class="hljs-literal">true</span>
            };
        }
        <span class="hljs-keyword">const</span> deleting_photo = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
            form.post(route(<span class="hljs-string">'admin.photos.delete'</span>, id))
            closeModal();
        }

        <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> {
            data.value.show_modal = <span class="hljs-literal">false</span>;


        }

        <span class="hljs-keyword">return</span> { form, data, closeModal, delete_photo, deleting_photo }

    }
});
&lt;/script&gt;
</code></pre>
<p>And here we have the HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">app-layout</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Dashboard"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-semibold text-xl text-gray-800 leading-tight"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

         <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-12"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-7xl mx-auto sm:px-6 lg:px-8"</span>&gt;</span>
              <span class="hljs-comment">&lt;!-- All posts goes here --&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl"</span>&gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 bg-sky-900 text-white rounded-md"</span> <span class="hljs-attr">href</span>&gt;</span>Create<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-col"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"</span>&gt;</span>
                          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"</span>&gt;</span>
                              <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"min-w-full divide-y divide-gray-200"</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">thead</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-50"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Photos<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>
                                              <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span>
                                              <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"</span>
                                          &gt;</span>Description<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative px-6 py-3"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
                                  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white divide-y divide-gray-200"</span>&gt;</span>
                                      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"photo in photos"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"photo.id"</span>&gt;</span>
                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                                                  <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>
                                              &gt;</span>{{ photo.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
                                                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-shrink-0 h-10 w-10"</span>&gt;</span>
                                                      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                                                          <span class="hljs-attr">class</span>=<span class="hljs-string">"h-10 w-10 rounded-full"</span>
                                                          <span class="hljs-attr">:src</span>=<span class="hljs-string">"photo.path"</span>
                                                          <span class="hljs-attr">alt</span>
                                                      /&gt;</span>
                                                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>

                                          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap"</span>&gt;</span>
                                              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm text-gray-900"</span>&gt;</span>
                                                {{ photo.description.slice(0, 100) + '...' }}
                                              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                                          <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                        <span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
                                         <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
                                            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
                                            View - Edit - 

                                            <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
                                                Delete
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                                            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                                        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                                      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
                                  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
                              <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
                          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">JetDialogModal</span> <span class="hljs-attr">:show</span>=<span class="hljs-string">"data.show_modal"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">title</span>&gt;</span>
                Photo {{ data.photo.description.slice(0, 20) + '...' }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">content</span>&gt;</span>
                Are you sure you want to delete this photo?

            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">template</span> #<span class="hljs-attr">footer</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"closeModal"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4 py-2"</span>&gt;</span>Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"deleting_photo(data.photo.id)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Yes, I am sure!<span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">JetDialogModal</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">app-layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>That's it. If you did everything correctly you should be able to see all photos, create new photos as well as edit and delete them.</p>
<p>I will leave you some home work. Can you figure out how to implement the view and edit links before the delete button in the section below?</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- ACTIONS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-6 py-4 whitespace-nowrap text-right text-sm font-medium"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-indigo-600 hover:text-indigo-900"</span>&gt;</span>
    View - Edit - 

    <span class="hljs-tag">&lt;<span class="hljs-name">jet-danger-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"delete_photo(photo)"</span>&gt;</span>
        Delete
    <span class="hljs-tag">&lt;/<span class="hljs-name">jet-danger-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<h2 id="heading-wrapup-and-whats-next">Wrapup and What's next</h2>
<p>During this guide we took our first steps and learned how to build a single page application using Laravel as our backend framework and Vue3 for the front end. We glued them together with Inertia js and built a simple photo application that lets a user manage photos. </p>
<p>We are just at the beginning of a fantastic journey. Learning new technologies isn't easy, but thanks to their exaustive documentations we can keep up and build awesome applications. </p>
<p>Your next step to master Laravel, Vue3, Inertia and all the tech we have been using so far is to hit their documentation and keep learning. Use the app we have build if you want, and improve it or start over from scratch. </p>
<p>Just keep that in mind, coding is fun so relax and enjoy it.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This is just an overview of how I'd build a single page application using these technologies.</p>
<p>If you are familiar with server-side routing and Vuejs then you will enjoy bulding a single page application with Laravel, Inertia, and Vuejs. The learning curve isn't that steep plus you have great documentation to help you out.</p>
<p>I hope you've enjoyed this guide. If so, let me know and consider subscribing to my YouTube channel and following me on Twitter. And if you get stuck, get in touch for help.</p>
<p>You can find the source code for this guide <a target="_blank" href="https://bitbucket.org/fbhood/spa-with-laravel-9/src/master/">here</a>.</p>
<p><a target="_blank" href="https://twitter.com/Fab_Sky_Walker">Follow me on Twitter</a>
<a target="_blank" href="https://join.slack.com/t/fabiopacificicom/shared_invite/zt-rf4vwvcm-esx1RkokwrJ93yyr1rPpVQ">Join me on slack</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Props in Vue.js ]]>
                </title>
                <description>
                    <![CDATA[ We use props to pass information/data from a parent component to child components. In this article, I will explain everything you need to know about props and why you should use props in Vue.js. Here's a brief outline of what we'll cover in this guid... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-props-in-vuejs/</link>
                <guid isPermaLink="false">66d45fb737bd2215d1e2458c</guid>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joel Olawanle ]]>
                </dc:creator>
                <pubDate>Wed, 11 Aug 2021 19:51:52 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/Yellow-and-Purple-Geometric-Covid-19-General-Facts-Twitter-Post.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We use props to pass information/data from a parent component to child components. In this article, I will explain everything you need to know about props and why you should use props in Vue.js.</p>
<p>Here's a brief outline of what we'll cover in this guide:</p>
<ul>
<li><p>What are props in Vue.js?</p>
</li>
<li><p>How to register props inside a component</p>
</li>
<li><p>How to work with multiple props</p>
</li>
<li><p>Vue.js prop types</p>
</li>
<li><p>How to pass data to props</p>
</li>
<li><p>How to pass functions to props</p>
</li>
<li><p>How to validate props</p>
</li>
<li><p>How to set default values for props</p>
</li>
</ul>
<h2 id="heading-what-are-props-in-vuejs">What are Props in Vue.js?</h2>
<p>“Props” is a special keyword which stands for properties. It can be registered on a component to pass data from a parent component to one of its children components.</p>
<p>This is a lot easier compared to using state management libraries like vuex for Vue.js applications.</p>
<p>Data in props can only flow one way – from the top, or parent component, to the bottom, or child components. This simply means you cannot pass data from a child to a parent.</p>
<p>Another thing to keep in mind is that Props are read-only and cannot be modified by the child component because the parent component "owns" that value.</p>
<p>Let’s balance things up now – the parent components pass props to the child component(s) while the child component emit events to the parent component(s).</p>
<h2 id="heading-how-to-register-props-inside-a-component">How to Register Props Inside a Component</h2>
<p>Let's now take a look at how we can register props inside a component.</p>
<pre><code class="lang-javascript">Vue.component(<span class="hljs-string">'user-detail'</span>, {
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'name'</span>],
  <span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;p&gt;Hi {{ name }}&lt;/p&gt;'</span>
})
.js
</code></pre>
<p>or, in a Vue Single File Component:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;p&gt;{{ name }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
<span class="hljs-built_in">export</span> default {
  props: [<span class="hljs-string">'name'</span>]
}
&lt;/script&gt;
</code></pre>
<p>In the above code, we registered a prop called <code>name</code> which we can call in the template section of our app.</p>
<p>Note: This is the child component and this prop is going to receive data from the parent component. I will explain this more later.</p>
<h2 id="heading-how-to-work-with-multiple-props">How to Work with Multiple Props</h2>
<p>You can have more than one prop by appending them to the props array, just like this:</p>
<pre><code class="lang-bash">Vue.component(<span class="hljs-string">'user-detail'</span>, {
  props: [<span class="hljs-string">'firstName'</span>, <span class="hljs-string">'lastName'</span>],
  template: <span class="hljs-string">'&lt;p&gt;Hi {{ firstName }} {{ lastName }}&lt;/p&gt;'</span>
})
</code></pre>
<p>or, in a Vue Single File Component:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;p&gt;Hi {{ firstName }} {{ lastName }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
<span class="hljs-built_in">export</span> default {
  props: [
    <span class="hljs-string">'firstName'</span>, 
    <span class="hljs-string">'lastName'</span>
  ],
}
&lt;/script&gt;
</code></pre>
<h2 id="heading-vuejs-prop-types">Vue.js Prop Types</h2>
<p>To specify the type of prop you want to use in Vue, you will use an object instead of an array. You'll use the name of the property as the key of each property, and the type as the value.</p>
<p>If the type of the data passed does not match the prop type, Vue sends an alert (in development mode) in the console with a warning. The valid types you can use are:</p>
<ul>
<li><p>String</p>
</li>
<li><p>Number</p>
</li>
<li><p>Boolean</p>
</li>
<li><p>Array</p>
</li>
<li><p>Object</p>
</li>
<li><p>Date</p>
</li>
<li><p>Function</p>
</li>
<li><p>Symbol</p>
</li>
</ul>
<pre><code class="lang-bash">Vue.component(<span class="hljs-string">'user-detail'</span>, {
  props: {
    firstName: String,
    lastName: String
  },
  template: <span class="hljs-string">'&lt;p&gt;Hi {{ firstName }} {{ lastName }}&lt;/p&gt;'</span>
})
</code></pre>
<p>or, in a Vue Single File Component:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;p&gt;Hi {{ firstName }} {{ lastName }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
<span class="hljs-built_in">export</span> default {
  props: {
    firstName: String,
    lastName: String
  },
}
&lt;/script&gt;
</code></pre>
<h2 id="heading-how-to-pass-data-to-props-in-vue">How to Pass Data to Props in Vue</h2>
<p>The major goal of using props is to pass down data/information. You can either pass your value as a data property using v-bind, such as in this code:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;ComponentName :title=title /&gt;
&lt;/template&gt;

&lt;script&gt;
<span class="hljs-built_in">export</span> default {
  //...
  <span class="hljs-function"><span class="hljs-title">data</span></span>() {
    <span class="hljs-built_in">return</span> {
      title: <span class="hljs-string">'Understanding Props in vuejs'</span>
    }
  },
  //...
}
&lt;/script&gt;
</code></pre>
<p>or as a static value like this:</p>
<pre><code class="lang-bash">&lt;ComponentName title=<span class="hljs-string">"Understanding Props in vuejs"</span> /&gt;
</code></pre>
<p>Suppose we are building an app which has so many buttons with different text/background colors. Instead of repeating the button syntax in all our files, it's better to create a button component and then pass the text/background colors as props.</p>
<p>Here's the parent component:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;div id=<span class="hljs-string">"app"</span>&gt;
    &lt;Button :name=<span class="hljs-string">'btnName'</span> bgColor=<span class="hljs-string">'red'</span> /&gt;
    &lt;Button :name=<span class="hljs-string">'btnName'</span> bgColor=<span class="hljs-string">'green'</span> /&gt;
    &lt;Button :name=<span class="hljs-string">'btnName'</span> bgColor=<span class="hljs-string">'blue'</span> /&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import Button from <span class="hljs-string">'./components/Button'</span>

<span class="hljs-built_in">export</span> default {
  name: <span class="hljs-string">'App'</span>,
  <span class="hljs-function"><span class="hljs-title">data</span></span>(){
    <span class="hljs-built_in">return</span>{
      btnName:<span class="hljs-string">"Joel"</span>,
    }
  },
  components: {
    Button
  }
}
&lt;/script&gt;
</code></pre>
<p>And here's the child component:</p>
<pre><code class="lang-bash">&lt;template&gt;
  &lt;button class=<span class="hljs-string">"btn"</span> :style=<span class="hljs-string">"{backgroundColor:bgColor}"</span>&gt;{{name}}&lt;/button&gt;
&lt;/template&gt;
&lt;script&gt;
<span class="hljs-built_in">export</span> default {
  name: <span class="hljs-string">'Button'</span>,
  props:{
    name:String,
    bgColor:String
  }
}
&lt;/script&gt;
</code></pre>
<p>The above code shows you how to use both data property and static values when you're getting data from a parent component and using that data in a child component.</p>
<p><strong>Note:</strong> you can also use a ternary operator inside the prop value to check a truthy condition and pass a value that depends on it.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">:tagUser</span>=<span class="hljs-string">"signedUp ? 'Logout' : 'Login'"</span> <span class="hljs-attr">bgColor</span>=<span class="hljs-string">'red'</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Button'</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'App'</span>,
  data(){
    <span class="hljs-keyword">return</span>{
      <span class="hljs-attr">signedUp</span>: <span class="hljs-literal">true</span>,
    }
  },
  <span class="hljs-attr">components</span>: {
    Button
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>In the above code, we are checking the <code>signedUp</code> data property. If it’s true, the data sent should be <strong>Logout</strong>, else it should be <strong>Login.</strong></p>
<h2 id="heading-how-to-pass-functions-to-props">How to Pass Functions to Props</h2>
<p>Passing a function or a method down to a child component as a prop is relatively straightforward. It's basically the same process as passing any other variable.</p>
<p>But there are reasons why you shouldn't use props as functions – instead you should use emit. This article properly explains <a target="_blank" href="https://michaelnthiessen.com/pass-function-as-prop/">why</a>.</p>
<pre><code class="lang-javascript">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ChildComponent</span> <span class="hljs-attr">:function</span>=<span class="hljs-string">"newFunction"</span> /&gt;</span></span>
&lt;/template&gt;
</code></pre>
<pre><code class="lang-javascript">&lt;script&gt;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">methods</span>: {
    newFunction() {
      <span class="hljs-comment">// ...</span>
    }
  }
};
&lt;/script&gt;
</code></pre>
<h2 id="heading-how-to-validate-props-in-vue">How to Validate Props in Vue</h2>
<p>Vue makes validating props very easy. All you have to do is add the required key and its value to the prop. We can validate with both the prop type and by using <code>required</code>:</p>
<pre><code class="lang-javascript">props: {
  <span class="hljs-attr">name</span>: {
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<h2 id="heading-how-to-set-default-prop-values">How to Set Default Prop Values</h2>
<p>Before concluding this article, let’s now see how to set default values to our props. Default values are rendered if the child component is unable to get data from the parent component.</p>
<p>Vue allows you to specify a default value, just as we specified <code>required</code> earlier.</p>
<pre><code class="lang-javascript">props: {
  <span class="hljs-attr">name</span>: {
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">default</span>: <span class="hljs-string">'John Doe'</span>
  },
  <span class="hljs-attr">img</span>: {
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">default</span>: <span class="hljs-string">'../image-path/image-name.jpg'</span>,
   },
}
</code></pre>
<p>You can also define the default value as an object. And it can be a function that returns an appropriate value, rather than being the actual value.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we have learned what props do and how props works in Vue.js.</p>
<p>In summary, we use props to pass down data from the parent components to the child component(s). The child component also emit events to the parent component(s) in case you need to send data/events from the child to the parent component.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/image-50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Thank you for reading!</p>
<p><strong>Useful Links</strong></p>
<ul>
<li><p><a target="_blank" href="https://flaviocopes.com/vue-props/">Vue.js Component Props - flaviocopes</a></p>
</li>
<li><p><a target="_blank" href="https://vuejs.org/v2/guide/components-props.html">Props - Vue documentation</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Migrate from Vue v.2 to Vue v.3 with a Simple Example Project ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific What is Vue.js? Vue.js is a progressive JavaScript frontend framework written by Evan You. It's one of the most powerful and easy to learn frameworks, and it has over 9.5 million downloads per month. In September 2020, Vue 3 core was... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/migrate-from-vue2-to-vue3-with-example-project/</link>
                <guid isPermaLink="false">66d45eeb868774922c884fe2</guid>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #vue-router ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 02 Jun 2021 19:14:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/Cover_migration_vue_2_3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<h2 id="heading-what-is-vuejs">What is Vue.js?</h2>
<p>Vue.js is a progressive JavaScript frontend framework written by Evan You. It's one of the most powerful and easy to learn frameworks, and it has over 9.5 million downloads per month.</p>
<p>In September 2020, Vue 3 core was released. The new Vue.js release introduces some cool new features but also some breaking changes.</p>
<h2 id="heading-why-should-i-migrate-to-vue3">Why should I migrate to Vue3?</h2>
<p>As the tech industry evolves, so do libraries, languages, and frameworks. At each release, bugs are fixed and new features introduced. And often with any major release, your workflow is enhanced. New features can give you the opportunity to do things that were tedious before.</p>
<p>Vue 3 is still relatively new. You don't have to migrate all your projects over, but as time goes by, support for version 2 might end. Because of this it's a good idea to know the steps you'll need to take to migrate your projects.</p>
<p>In this guide, I'll walk you through the basic steps you will need to follow to approach the migration. We will take a simple project and migrate it to Vue 3.</p>
<p>The project that we will use is intentionally simple, and so anyone can follow along. The more complex your project is, the more carefully you'll want to plan for the migration.</p>
<h2 id="heading-intro">Intro</h2>
<p>The new Vue.js version does come with quite a few breaking changes and new features. Also, popular libraries like Vue Router have been updated to support the new Vue version.</p>
<p>If you already know Vue 2, the basics are quite the same. But before you can migrate a project to Vue 3 there are changes you need to take into account.</p>
<p>Depending on the size of the project you want to migrate, make sure to consider all the changes introduced with the new release so that your application will still work after the migration.</p>
<p>For this tutorial, I'll keep things simple and show you how to migrate a Vue.js project that currently uses the Vue 2 CDN.</p>
<p>I'm taking the project from the book I wrote for freeCodeCamp, which you can find <a target="_blank" href="https://www.freecodecamp.org/news/build-a-portfolio-with-vuejs/">here</a>. </p>
<p>In that project we used Vue Router, so we will also look at Vue router's changes in this article.</p>
<h2 id="heading-what-you-need-to-follow-along-with-this-article">What you need to follow along with this article</h2>
<p>To follow along you need a basic knowledge of Vue.js and Vue Router. If you don't have that. then I suggest you start by checking out my book available on <a target="_blank" href="https://www.freecodecamp.org/news/build-a-portfolio-with-vuejs/">freeCodeCamp</a>.</p>
<p>You can also find the playlist with the full 8 hour course available for free on my <a target="_blank" href="https://www.youtube.com/playlist?list=PL-qez5yxvgfjYZE_BP7WyxZuLyVPyWrF1">YouTube channel</a>.</p>
<h2 id="heading-what-well-cover-in-this-article">What we'll cover in this article</h2>
<p>This tutorial is organised into three main chapters. First, we will have a look at the changes in Vue.js v3.x then a quick overview of Vue Router v4.x. And finally, we will start planning the migration of a real project.</p>
<ul>
<li>Vue v3.x overview<ul>
<li>breaking changes</li>
</ul>
</li>
<li>Vue Router v4.x overview<ul>
<li>breaking changes</li>
</ul>
</li>
<li>Portfolio Project Migration<ul>
<li>Clone the Repo</li>
<li>Update CDN scripts</li>
<li>Update Vue instance</li>
<li>Update Vue Router instance</li>
</ul>
</li>
</ul>
<p>Here's the video version of this article if you want to follow along there:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/5y8-fKSY_Lg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Watching the video will help you reinforce your learning while reading the steps below. Here you can find the final <a target="_blank" href="https://bitbucket.org/fbhood/advanced-vuejs/src/master/">repository</a> for the project.</p>
<h2 id="heading-vue-v3x-overview">Vue v3.x Overview</h2>
<p>Vue 3 introduces a few new features and a bunch of breaking changes. Let's see how these changes will affect our application and consider them before migrating.</p>
<h3 id="heading-vue-v3x-breaking-changes">Vue V3.x Breaking Changes</h3>
<p>In Vue 3 the breaking changes basically fall into seven categories:</p>
<ul>
<li>Global API
(responsible for how Vue behaves) - it's highly likely that you want to look at these changes.</li>
<li>Template Directives
(Changes made to how v- directives work) - it's highly likely that you want to look at these changes.</li>
<li>Components
(Changes to how components work) - if your application uses components it's highly likely that you want to look at these changes</li>
<li>Render Function (Lets you create HTML elements programmatically)</li>
<li>Custom Elements (Tells Vue about the creation of custom HTML elements)</li>
<li>Minor Changes (These might not affect you, but you'll still want to look into these)</li>
<li>Removed APIs (Things that are no longer available in Vue 3)</li>
</ul>
<p>Among all the changes there are some of them that any application will use, like the Global API and components. So you will need to take them into account if you want to start using the new Vue release.</p>
<p>And it's worth mentioning the following additional changes:</p>
<ul>
<li>The way you create Vue Applications and Component Instances has changed (Global API)</li>
<li>You should always declare the data option as a function (minor change)</li>
<li>Change of precedence when using v-if and v-for on the same element (template Ddrectives)</li>
<li>You should declare an emits option for component events (components)</li>
</ul>
<p>For a complete list of changes, you can head over to the <a target="_blank" href="https://v3.vuejs.org/guide/migration/introduction.html#breaking-changes">documentation</a></p>
<p>Let's look at some of these changes in more detail now.</p>
<h3 id="heading-how-to-create-application-and-component-instances-in-vue-3">How to Create Application and Component Instances in Vue 3</h3>
<p>In Vue 3 the way you create an app has changed. The Vue app now uses the new <code>.createApp()</code> method to create application instances. </p>
<p>The Vue application is now considered a root component, so the way you define its data options has changed as well.</p>
<p>The HTML root element hasn't changed, so inside an index.html file, you will still see something like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the JavaScript file, there is an important change you need to understand: You will no longer use <code>new Vue()</code> to create a new app instance but instead you'll use a new method called <code>createApp()</code>:</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Vue 3 syntax</span>

<span class="hljs-keyword">const</span> app = Vue.createApp({
    <span class="hljs-comment">// options object</span>
})
app.mounth(<span class="hljs-string">'#app'</span>) <span class="hljs-comment">// Vue Instance - Root component</span>

<span class="hljs-comment">// Vue 2 syntax</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// options object</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>
})
</code></pre>
<h3 id="heading-how-to-define-a-component-in-vue-3">How to Define a Component in Vue 3</h3>
<p>To define a component in Vue 3, you no longer use <code>Vue.component()</code>. Instead you now use the application root component, like so:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Vue 3 syntax */</span>
<span class="hljs-keyword">const</span> app = Vue.createApp({
    <span class="hljs-comment">// options here</span>
})

app.component(<span class="hljs-string">'componenet-name'</span>, {
    <span class="hljs-comment">// component code here</span>
})


<span class="hljs-comment">/* Vue 2 syntax*/</span>
Vue.component(<span class="hljs-string">'component-name'</span>, {
    <span class="hljs-comment">// component code here</span>
})
</code></pre>
<h3 id="heading-how-to-use-the-data-options-object-in-vue-3">How to Use the Data Options Object in Vue 3</h3>
<p>Given that the main app instance is now considered a root component, you can no longer specify the data property as an object. Instead, you need to define it as a function that returns an object like you usually do in components.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Vue 3</span>
<span class="hljs-keyword">const</span> app = Vue.createApp({
    <span class="hljs-comment">// options object</span>
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">message</span>: <span class="hljs-string">'hi there'</span>
        }
    }
})
app.mounth(<span class="hljs-string">'#app'</span>) <span class="hljs-comment">// Vue Instance - Root component</span>

<span class="hljs-comment">// Vue 2 syntax</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// options object</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">message</span>: <span class="hljs-string">'hi there'</span>
    }
})
</code></pre>
<h3 id="heading-change-of-precedence-for-v-ifv-for-in-vue-3">Change of Precedence for v-if/v-for in Vue 3</h3>
<p>In Vue 2 if you used both directives on the same element, the v-for directive would take precedence over v-if. But in Vue 3 v-if always takes precedence.</p>
<p>However, using both directives isn't a great idea. Make sure to visit the documentation <a target="_blank" href="https://v3.vuejs.org/guide/migration/v-if-v-for.html#overview">here</a> to learn more.</p>
<h3 id="heading-how-to-use-the-emits-property-on-component-events-in-vue-3-breaking-changenew-feature">How to Use the Emits Property on Component Events in Vue 3 (breaking change/new feature)</h3>
<p>Similar to the <code>props</code> property, now in Vue 3 there is also an <code>emits</code> property that a component can use to declare the events that it can emit to the parent component.</p>
<p>I strongly recommend using this property to avoid emitting events twice in components that need to re-emit native events, like a click event.</p>
<p>Here is an example from the official documentation:</p>
<pre><code class="lang-js">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ text }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"$emit('accepted')"</span>&gt;</span>OK<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">props</span>: [<span class="hljs-string">'text'</span>],
    <span class="hljs-attr">emits</span>: [<span class="hljs-string">'accepted'</span>]
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>The emits property can also accept an object.  </p>
<p>I won't go in-depth into this just yet, but I'll tackle each of the features/changes in a dedicated video series sooner or later, I promise.</p>
<h2 id="heading-vue-router-v4x-overview">Vue Router v4.x Overview</h2>
<p>With the new release of Vue.js, we also have a new version of Vue Router. The new release v4.x has some breaking changes that you'll need to consider if you want to migrate a project to the new Vue release.</p>
<h3 id="heading-vue-router-v4-breaking-changes">Vue Router V4 Breaking Changes</h3>
<p>Two breaking changes are especially worth mentioning, since they are at the base of a Vue Router application. You will need to know about them to migrate your application later.</p>
<ul>
<li>The Vue Router instance has changed</li>
<li>Theres a new history option</li>
</ul>
<p>The full list of changes is available <a target="_blank" href="https://next.router.vuejs.org/guide/migration/index.html">here</a>.</p>
<p>Let's look at these two changes in depth.</p>
<h3 id="heading-the-vue-router-4-instance-has-changed">The Vue Router 4 Instance Has Changed</h3>
<p>To create a new Vue Router instance, you no longer use the VueRuter function constructor.</p>
<p>Here is the Vue Router v.3x <a target="_blank" href="https://router.vuejs.org/guide/#javascript">documentation</a> so you can compare.</p>
<p>The code changes from this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 3. Create the router instance and pass the `routes` option</span>
<span class="hljs-comment">// You can pass in additional options here, but let's</span>
<span class="hljs-comment">// keep it simple for now.</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
  routes <span class="hljs-comment">// short for `routes: routes`</span>
})

<span class="hljs-comment">// 4. Create and mount the root instance.</span>
<span class="hljs-comment">// Make sure to inject the router with the router option to make the</span>
<span class="hljs-comment">// whole app router-aware.</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
  router
}).$mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<p>To this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 3. Create the router instance and pass the `routes` option</span>
<span class="hljs-comment">// You can pass in additional options here, but let's</span>
<span class="hljs-comment">// keep it simple for now.</span>
<span class="hljs-keyword">const</span> router = VueRouter.createRouter({
  <span class="hljs-comment">// 4. Provide the history implementation to use. We are using the hash history for simplicity here.</span>
  <span class="hljs-attr">history</span>: VueRouter.createWebHashHistory(), <span class="hljs-comment">// &lt;-- this is a new property and it is mandatory!</span>
  routes, <span class="hljs-comment">// short for `routes: routes`</span>
})

<span class="hljs-comment">// 5. Create and mount the root instance.</span>
<span class="hljs-keyword">const</span> app = Vue.createApp({})
<span class="hljs-comment">// Make sure to _use_ the router instance to make the</span>
<span class="hljs-comment">// whole app router-aware.</span>
app.use(router)

app.mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<p>In the code above, to create a new Vue router instance you now have to use the VueRouter object and call the <code>createRouter()</code> method. </p>
<p>Also, the new history property is mandatory – <code>history: VueRouter.createWebHashHistory()</code>. You must define it or you will get a console error.</p>
<p>Next, you'll create the Vue instance using the <code>createApp()</code> method and use the variable <code>app</code> to call the <code>.use()</code> method. You pass the router instance there that you created in the previous step.</p>
<p>Finally, you can mount the root DOM element on the app instance using <code>app.mount('#app')</code>.</p>
<p>You can read the Vue router v4.x <a target="_blank" href="https://next.router.vuejs.org/guide/#javascript">documentation</a> for more details.</p>
<h2 id="heading-how-to-migrate-a-portfolio-project-from-vue-2-to-vue-3">How to Migrate a Portfolio Project from Vue 2 to Vue 3</h2>
<p>You can see how to do this in the video on <a target="_blank" href="https://youtu.be/5y8-fKSY_Lg">YouTube</a> if you'd like to follow along.</p>
<p>Considering all the above, and after a careful review of the breaking changes, let's try to upgrade one of our projects my Vue course. I'll use the Portfolio, the final project of the course.</p>
<p>We'll need to:</p>
<ul>
<li>Clone the repo</li>
<li>Update the CDN scripts</li>
<li>Update the Vue instance</li>
<li>Update the Vue Router instance</li>
</ul>
<p>To migrate our app to Vue 3 we will definitely need to update the following:</p>
<ul>
<li>Vue Application instance</li>
<li>Vue-Router instance</li>
<li>CDN Links</li>
</ul>
<p>Let's take it step by step.</p>
<h3 id="heading-clone-the-project-repository">Clone the Project Repository</h3>
<p>First, make sure you clone the repo inside the current folder:</p>
<pre><code class="lang-sh">git <span class="hljs-built_in">clone</span> https://bitbucket.org/fbhood/vue-folio/src/master/ vue-folio
</code></pre>
<p>Since our project still uses the CDN, the next step is to update its links.</p>
<h3 id="heading-update-the-projects-cdn">Update the Project's CDN</h3>
<p>In our project we're using both the Vue CDN and the Vue Router CDN, so let's update them both.</p>
<p>Open the index.html file and replace this:</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- VueJS 3 production version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>with this:</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- VueJS 3 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue@3"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@4"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-update-the-code">Update the Code</h3>
<p>Now if you open the project with the live server and open the inspector, you will notice that the application doesn't show up and there are two errors in the console. Both seem related to the Vue router:</p>
<pre><code class="lang-js">You are running a development build <span class="hljs-keyword">of</span> Vue.
Make sure to use the production build (*.prod.js) when deploying <span class="hljs-keyword">for</span> production.

Uncaught <span class="hljs-built_in">TypeError</span>: <span class="hljs-built_in">window</span>.Vue.use is not a <span class="hljs-function"><span class="hljs-keyword">function</span>
    <span class="hljs-title">at</span> <span class="hljs-title">vue</span>-<span class="hljs-title">router</span>.<span class="hljs-title">js</span>:1832
    <span class="hljs-title">at</span> <span class="hljs-title">vue</span>-<span class="hljs-title">router</span>.<span class="hljs-title">js</span>:9
    <span class="hljs-title">at</span> <span class="hljs-title">vue</span>-<span class="hljs-title">router</span>.<span class="hljs-title">js</span>:10

<span class="hljs-title">Uncaught</span> <span class="hljs-title">ReferenceError</span>: <span class="hljs-title">VueRouter</span> <span class="hljs-title">is</span> <span class="hljs-title">not</span> <span class="hljs-title">defined</span>
    <span class="hljs-title">at</span> <span class="hljs-title">main</span>.<span class="hljs-title">js</span>:185</span>
</code></pre>
<p>Vue router?! Why?</p>
<p>Well, remember that when Vue was rewritten, its libraries had to update their codebases, too. So don't forget those breaking changes related to Vue-router since our application uses it.</p>
<p>Let's first update the main Vue instance to use the new syntax. Then we will look at what changes we need to make so that Vue Router works.</p>
<p>Update this code inside the main.js file from this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create and mount the Vue instance</span>

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<p>to this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create and mount the Vue instance</span>

<span class="hljs-keyword">const</span> app = Vue.createApp({
    router
})
app.mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<h3 id="heading-vue-router-4-changes">Vue Router 4 Changes</h3>
<p>Above we saw the new syntax to define the root Vue instance component. But now, since we are using the Vue router, we need to take into account its <a target="_blank" href="https://next.router.vuejs.org/guide/migration/index.html">breacking changes</a> too.</p>
<h4 id="heading-the-way-vue-router-is-instantiated-has-changed">The way Vue Router is instantiated has changed</h4>
<p>It changed from this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})
</code></pre>
<p>to this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = VueRouter.createRouter({
    <span class="hljs-comment">// Provide the history implementation to use. We are using the hash history for simplicity here.</span>
    <span class="hljs-attr">history</span>: VueRouter.createWebHashHistory(),
    routes, <span class="hljs-comment">// short for `routes: routes`</span>
})
</code></pre>
<p>The code above deals with two major changes: <code>new VueRouter()</code> has been replaced by <code>VueRouter.createRouter()</code>, and the new <code>history</code> option now replaces <code>mode</code>.</p>
<p>Visit the <a target="_blank" href="https://next.router.vuejs.org/guide/#html">documentation</a> for Vue Router 4 to find out more.</p>
<p>Finally, let's make our app aware that we are using Vue Router. If we injected the router instance in the Vue app, now we need to instruct it to use the Vue router, use the <code>.use()</code> method to do so, and pass to it the router instance.</p>
<p>Change from this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create and mount the Vue instance</span>

<span class="hljs-keyword">const</span> app = Vue.createApp({
    router
})
app.mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<p>to this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// create and mount the Vue instance</span>

<span class="hljs-keyword">const</span> app = Vue.createApp({})
app.use(router)
app.mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<p>And there you have it!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It doesn't really matter how complex your Vue application is – if you want to migrate to a new major release you will still need to plan for it, read the release notes, and go over all the breaking changes to make sure that you understand what will break. </p>
<p>The more complex the application is and more carefully you should plan your migration.</p>
<p>For our simple application that's all there is to do. But it's not always like that. So get ready and plan in advance.</p>
<p>If you enjoyed this guide please share the article and remember to subscribe to my <a target="_blank" href="https://youtube.com/channel/UCTuFYi0pTsR9tOaO4qjV_pQ">YouTube channel</a>. Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a Portfolio with Vue.js Axios, the GitHub REST API, and Netlify ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects.  If you follow this tutorial to the end, you will b... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-portfolio-with-vuejs/</link>
                <guid isPermaLink="false">66d45edcb3016bf139028d31</guid>
                
                    <category>
                        <![CDATA[ axios ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 12 May 2021 20:53:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/VUE.JS-_-Article-Cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects. </p>
<p>If you follow this tutorial to the end, you will build a simplified version of Twitter and a single page application for a portfolio using the GitHub API.</p>
<h2 id="heading-what-you-need-to-know-to-follow-this-tutorial">What you need to know to follow this tutorial</h2>
<p>To follow along, you will need at least some basic knowledge of HTML, CSS, and JavaScript. </p>
<p>Knowledge of Vue.js isn't required, as you will learn the basics first and then we'll move into building the projects together.</p>
<p>At the end of each section, you'll find that information in video form via a YouTube link/embed. That way you can watch the videos to cement your knowledge of what you just read.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-introduction">Introduction</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-vue">How to Install Vue</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-vue-instance">How to Create a Vue Instance</a></li>
<li><a class="post-section-overview" href="#heading-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</a></li>
<li><a class="post-section-overview" href="#heading-vue-directives">Vue Directives</a></li>
<li><a class="post-section-overview" href="#heading-methods-in-vue">Methods</a></li>
<li><a class="post-section-overview" href="#heading-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals</a></li>
<li><a class="post-section-overview" href="#heading-loops-in-vue">Loops</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to handle user inputs with events Handling</a></li>
<li><a class="post-section-overview" href="#heading-two-way-model-binding-v-model-in-vue">Two way model binding (v-model)</a></li>
<li><a class="post-section-overview" href="#heading-computed-properties-and-methods">Computed Properties and methods</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-simple-twitter-clone"><strong>Project</strong>: Simple Twitter Clone</a></li>
<li><a class="post-section-overview" href="#heading-vue-component-basics">Component basics</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-your-simpletwitter-project-with-components">Project Update: Simple Twitter clone with components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-perform-api-calls-with-axios">Axios and RestAPI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-routing-with-vuerouter">Routing with VueRouter</a></li>
<li><a class="post-section-overview" href="#heading-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify"><strong>Final Project</strong>: build a Portfolio with VueJS, VueRouter, Axios, GitHub API</a></li>
<li><a class="post-section-overview" href="#heading-continuos-deployment-with-bitbucket-and-netlify"><strong>Deploy</strong> Continuous deployment with BitBucket and Netlify</a></li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>VueJS is a JavaScript framework that has become really popular in recent years. </p>
<p>In this guide, we will start by looking at the fundamentals first, with a quick look at two libraries: VueRouter and Axios. We will use them to build a cool portfolio project at the end.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/CzgP6GamIMc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Click to view the video on <a target="_blank" href="https://youtu.be/CzgP6GamIMc">YouTube</a>.</p>
<h2 id="heading-how-to-install-vue">How to Install Vue</h2>
<p>You can use Vue in your projects by installing it using a package manager like NPM or by using its CDN. If you've never used Vuejs before, I suggest that you use the CDN, as it will be easier if you want to code along with me. </p>
<p>Click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/1-installation/">Repository</a></p>
<p>Click to view the <a target="_blank" href="https://youtu.be/enz0Vi3NuDA">YouTube-Video</a> or find it at the end of this section to reinforce what you've learned.</p>
<h3 id="heading-the-vue-cdn">The Vue CDN</h3>
<p>For the CDN, we only need to include the script tag below inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Development version for prototyping and learning --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Alternatively, you can use a production-ready script that uses a specific stable release, like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Production version --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>In production, Vue suggests using the optimized version to replace vue.js with vue.min.js.</p>
<p>There is also an ES Modules-compatible build:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'</span>
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-install-vue-via-npm">How to Install Vue via NPM</h3>
<p>If you plan to build large scale applications, I recommend installing via NPM like this:</p>
<pre><code class="lang-bash">npm install vue
</code></pre>
<p>As I said above, we will use the Vue CDN so that anyone can follow this guide. So our final HTML file will look something like this: </p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS Tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- vue development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>



    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Let's break this code down. First, we've add a basic markup for an HTML file. Then we've included the script tag for the VueJs framework. </p>
<p>In the end, before closing the body tag, we've added our main.js script where we placed all the JavaScript code for our application. </p>
<p>Let's now move to the next step and add our first Vue instance inside the main.js file.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/enz0Vi3NuDA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-create-a-vue-instance">How to Create a Vue Instance</h2>
<p>Once you've installed Vue or included it via its CDN, you can create a Vue instance. You can do that using the <code>new Vue()</code> function. This function accepts an object of options.</p>
<p>If you read the documentation, you will see that the vue instance is often stored inside a variable called <code>vm</code>, but you can call it anything you like. I'll call it <code>app</code> during this guide.</p>
<p>So now, inside the main.js file you need to create a variable and store in it the Vue instance like so:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options goes here</span>
})
</code></pre>
<p>The object you pass to the Vue instance is called the options object.
Inside the options object, you can add all the options described in the Vue API reference pages to build our application. </p>
<p>The options object has properties divided into multiple sections: </p>
<ul>
<li>Data </li>
<li>DOM </li>
<li>Life Cycle Hooks </li>
<li>Assets</li>
<li>Composition </li>
<li>Misc categories </li>
</ul>
<p>The first property that you need to build you Vue application is used to connect Vue with a root DOM element. Then you will need some data options to work with.</p>
<p>Let's start by connecting the Vue instance with a root DOM element.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/2-create-vue-instance/">Repository</a> here.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/gBJaL7Jqh4w">YouTube-Video</a> or find it at the end of this section so you can review what you've learned.</p>
<h3 id="heading-optionsdom-how-to-select-the-root-dom-element">Options/DOM: How to select the root DOM element</h3>
<p>The Options/DOM API gives you an <code>el</code> property that you can use to select an existing DOM element that Vue will use to mount your application instance. </p>
<p>The <code>el</code> property accepts a string that contains a CSS selector for the element or directly a DOM element.</p>
<p>NOTE: Vue discourages using the body or HTML tags and suggests using a different element as a mounting point.</p>
<p>Let's do it. Inside the body of the index.html file, you need to put the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now you have a root element that you can use to connect the Vue instance.
Back inside the main.js file, let's select this element inside the options object. </p>
<p>You can now use the <code>el</code> property to select the element you created with an id of <code>app</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
})
</code></pre>
<p>You now have an element to work with. You can move on to the next step and add to the options object the data object.</p>
<p>You can read more about it in the documentation here:[https://vuejs.org/v2/api/#Options-DOM]</p>
<h3 id="heading-optionsdata-how-to-add-the-data-object-or-function-when-used-in-a-component">Options/Data: How to add the data object (or function when used in a component)</h3>
<p>When a new instance is created, it adds all properties found in its data object to the Vue reactivity system. And when a value in the data object changes, the view will reflect these changes. This is at the base of the VueJS reactivity system. </p>
<p>To explain it, let's see a practical example.</p>
<h4 id="heading-create-a-data-object">Create a data object</h4>
<p>Inside the main.js file you can create a data property that has an object as its value, like so:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {}
})
</code></pre>
<p>The data object can be defined directly inside the Vue instance like in the code above, or outside the instance like in the code below. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> dataObject = {}
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: dataObject
})
</code></pre>
<p>You can pick the one you like.</p>
<h4 id="heading-add-properties-to-the-data-object">Add properties to the Data Object</h4>
<p>Since VueJs is a JavaScript framework, it's helpful to remember that what you know about JavaScript is still valuable here. </p>
<p>Vue is just a JavaScript object that has a number of methods and properties that you can use to simplify and speed up your workflow.</p>
<p>Let's add some properties to the data object to see how it works.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a data object</span>
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-comment">// create a vue instance, add the data property and the dataObject created</span>
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">alert</span>: <span class="hljs-string">"This is an alert message! "</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ];
    }
})
</code></pre>
<p>With the code above, you simply add two properties to the data object: an <code>alert</code> property and a  <code>projects</code> property. </p>
<p>The alert property just a string while the projects property is an array of objects. </p>
<p>Now that you have some data to work with, let's see how you can access and modify their values.</p>
<h4 id="heading-manipulate-properties-in-the-data-object">Manipulate properties in the data object</h4>
<p>You can access and manipulate the properties of a data object using the variable that contains the Vue instance <code>app</code>. Then you can reference the properties using dot notation, like <code>app.alert</code>. </p>
<p>In the browser, if you open the console you can see that when you write <code>app</code> you get the Vue instance object. So, like any other object with dot notation, you get its properties and methods.</p>
<p>Let's try this out inside the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the alert property in the data object</span>
app.alert <span class="hljs-comment">// This is an alert message!</span>
<span class="hljs-comment">// update a data property value</span>
app.alert = <span class="hljs-string">"This is a new alert message!"</span> 
app.projects
</code></pre>
<p>The code above does three simple things:</p>
<ul>
<li>the first line accesses the <code>alert</code> property and prints its content "this is an alert message"</li>
<li>the second line assigns a new value to the <code>alert</code> property with the equals operator</li>
<li>finally, the third line returns the value of the projects array. </li>
</ul>
<p>You can also access the entire data object using the shortcuts $data or _data</p>
<p>Back in the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the entrie data object</span>
app.$data <span class="hljs-comment">// {__ob__: Observer} option 1</span>
app._data <span class="hljs-comment">// {__ob__: Observer} option 2</span>
</code></pre>
<p>You can read more about this in the documentation here: [https://vuejs.org/v2/api/#Options-Data]</p>
<h3 id="heading-options-data-methods">Options Data Methods</h3>
<p>The Vue instance gives you access to a number of properties and methods.
You can access default methods and properties using the <code>$</code> sign. It is used to differentiate Vue defined methods from those defined by the user.</p>
<p>There are a number of instance methods and properties predefined and split into four different categories:</p>
<ul>
<li>Instance Properties</li>
<li>Instance Methods / Data</li>
<li>Instance Methods / Events</li>
<li>Instance Methods/life cycle hooks</li>
</ul>
<p>For instance, with the following code, you can get the <code>data</code> and <code>options</code> objects or access the <code>watch</code> or the <code>on</code> methods. </p>
<pre><code class="lang-js">app.$data <span class="hljs-comment">// returns the data object</span>
app.$options <span class="hljs-comment">// returns the options object</span>
app.$watch() <span class="hljs-comment">// function that watched for changes on the vue instance</span>
app.$on() <span class="hljs-comment">// listen for a custom event on the vue instance</span>
</code></pre>
<p>I won't dive deeper into this since it's out of the scope of this guide. But if you are interested and want to learn more, here is the <a target="_blank" href="https://vuejs.org/v2/api/#Instance-Properties">documentation</a>.</p>
<h3 id="heading-lifecycle-hooks">Lifecycle Hooks</h3>
<p>Vue gives you access to a series of functions called lifecycle hooks. They allow you to run code at specific stages of the Vue initialization steps.</p>
<p>Inside all lifecycle hooks you have access to their <code>this</code> variable that points to the Vue instance. </p>
<p>You will see how this works in more detail in future sections. But for now this is a short summary of the available hooks and what they let you do:</p>
<ul>
<li>beforeCreate (you can run code before the Vue instance is created)</li>
<li>created (you can run code after the Vue instance is created )</li>
<li>beforeMount (you can run code before your element is mounted to the DOM)</li>
<li>mounted (you can run code when the element is mounted to the DOM)</li>
<li>beforeUpdate (you can run code before values are updated in the DOM)</li>
<li>updated (you can run code after values in the DOM have been updated)</li>
<li>beforeDestroy (you can run code before an instance is destroyed)</li>
<li>destroyed (you can run code when an instance is destroyed)</li>
</ul>
<p>During the course, we will often use the mounted hook. If you are curious to learn more about this topic, I suggest you look at the diagram in the documentation first. Find the lifecycle Hooks <a target="_blank" href="https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram">diagram</a> here.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gBJaL7Jqh4w" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</h2>
<p>VueJS uses mustache syntax <code>{{ }}</code> to render data from the Vue instance inside the HTML element. </p>
<p>Using this syntax you can grab properties and methods defined in the Vue instance. The property is then parsed and rendered to the page.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/3-work-with-templates/">Repository here</a>.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/pDj3SQ8TNzs">YouTube-Video here</a> here, or find it at the end of this section to review what you've just learned.</p>
<h3 id="heading-text-data-binding">Text Data binding</h3>
<p>This is called text data binding. Let's see an example of how you can bind data between the Vue instance and your template file. </p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{ title }}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The code above has an <code>h1</code> tag inside the root element with an id of <code>app</code> we defined in the previous chapter. </p>
<p>Inside the <code>h1</code> tag you use the double curly brackets syntax to render onto the page the value of a property in the data object called that you called <code>title</code>. </p>
<p>You don't have a <code>title</code> property yet inside your data object, so let's add it.</p>
<p>Inside the main.js file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now, with the code above you can render the content of the property <code>title</code> inside the <code>h1</code> tag in your template. The final result will be something like this: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>However, with this method, you can only pass a string. If you want to use HTML tags inside the string these will not be parsed but instead will be shown as simple strings.</p>
<p>For instance if you assign the following string to the <code>title</code> property</p>
<pre><code class="lang-js">    title: <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>
</code></pre>
<p>And then try to render it inside our HTML like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>The property <code>title</code> will be rendered as a plain string including the HTML tags 
ie. <code>John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;</code></p>
<p>Of course you can parse HTML too.</p>
<h3 id="heading-how-to-parse-raw-html">How to parse raw HTML</h3>
<p>To render a raw HTML element we need to introduce another important Vue concept called directives. </p>
<p>In this case, you will use the v-html directive inside your HTML tag as an attribute and pass to it the property title. </p>
<p>When you're using Vue directives, the text inside the quotes is considered a JavaScript expression. This means that it's computed and its result is rendered.  </p>
<p>Let's create a separate property for the title with HTML tags inside so that you can see how both render onto the page. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now inside your HTML file, you will use this <code>{{}}</code> syntax to render the property <code>title</code>. But on the tag where you want to render raw HTML, with the <code>titleHTML</code> property, you use the v-html directive instead.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{ title }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-html</span>=<span class="hljs-string">"titleHTML"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Both elements will now render correctly including the second property that has HTML tags inside.</p>
<p>NOTE: Rendering HTML can expose XSS vulnerabilities. Never use this approach on user-provided content.</p>
<p>Now that you know how to render data onto the page, let's dig deeper into directives. </p>
<p>If you want to read more, visit the documentation 
<a target="_blank" href="https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions">here</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pDj3SQ8TNzs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-vue-directives">Vue Directives</h2>
<p>Inside your HTML files, you can use directives to interact with HTML attributes. A directive applies effects to the DOM when its expression changes. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/4-Directives/">Repository here</a></p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/LICvNmhsTEs">YouTube-Video here</a>, or you can find it at the end of this section to review what you've learned.</p>
<h3 id="heading-the-v-bind-directive-on-html-attributes">The v-bind directive on HTML attributes</h3>
<p>So far, you have used the <code>{{}}</code> syntax to render something between HTML opening and closing tags. But inside an HTML tag you cannot use the {{ }} syntax. </p>
<p>So how do you connect an HTML attribute to the Vue instance? You use the v-bind directive instead which lets you access data object properties as you have done before. </p>
<p>The v-bind directive is one of the directives that take arguments which are specified after the colon. In our case here, what's specified after the colon is the HTML attribute name like id, class, href, src and so on. </p>
<p>If you need to dynamically assign an attribute like href or even a class, you can bind it with the Vue instance using the v-bind directive. It will then be able to get what's in the options object, like properties in the data object.</p>
<p>Let's see v-bind in action and start connecting the <code>id</code> and <code>class</code> attributes so you can assign them values dynamically with Vue.</p>
<p>Inside our index.html file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-bind:class</span>=<span class="hljs-string">"dynamicClass"</span> <span class="hljs-attr">v-bind:id</span>=<span class="hljs-string">"dynamicId"</span>&gt;</span>Dinamically assign a class and an id to the div<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break the code above and see what it's doing. </p>
<p>First, you have a <code>div</code> tag inside the root element. Then you use the v-bind directive on the class and the id attributes. </p>
<p>Inside the quotes, you specify two properties that later you'll define inside the data object of your Vue instance.</p>
<p>Remember that when using Vue directives the content between quotes is treated as a JavaScript expression.</p>
<p>Let's define these two properties inside the Vue instance.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],
        <span class="hljs-attr">dynamicId</span> : <span class="hljs-string">"projects_section"</span>,
        <span class="hljs-attr">dynamicClass</span> : <span class="hljs-string">"projects"</span>
    }
})
</code></pre>
<p>So you have defined <code>dinamicId: "projects_section"</code> and <code>dynmicClass: "projects"</code> properties and assigned them two values.</p>
<p>Thanks to the data binding on the attributes, your HTML tag will be rendered as follows (and you can now dynamically change your attributes values and see them change reactively):</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects_section"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span>&gt;</span>Dynamically assign a class and an id to the div<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-v-bind-with-boolean-values">V-bind with Boolean values</h3>
<p>With attributes using a boolean value, the v-bind directive works differently. It will show the attribute only if the property's value is true. In all other cases, it won't render the attribute and its content. </p>
<p>For the next example, you will use a button with the disabled attribute.</p>
<p>Inside your root HTML element:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-bind:disabled</span>=<span class="hljs-string">"disabled"</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside a Vue instance:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-comment">//disabled: false, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: null, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: undefined, // wont render the attribute</span>
    <span class="hljs-attr">disabled</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// renders the attribute</span>
}
})
</code></pre>
<p>Only if the disabled property is set to true does the attribute become visible and render its property content.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">disabled</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>This is something to keep in mind when working with such attributes.</p>
<p>Another thing to consider is that bindings can include a single JavaScript expression, with some restrictions:</p>
<ul>
<li>only expressions are allowed</li>
<li>only a single expression</li>
<li>no statements</li>
<li>no flow control tools, but the ternary operator works.</li>
</ul>
<p>If you want to read more, visit the documentation 
here:[https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions]</p>
<p>So far we have seen only two Vue directives, v-html and v-bind. But there are a number of directives available, and here are some more (to list just a few):</p>
<ul>
<li>v-html</li>
<li>v-bind</li>
<li>v-if</li>
<li>v-else-if</li>
<li>v-else</li>
<li>v-for</li>
<li>v-on
All directives have a v- prefix, but there is shorthand for v-bind (:) and v-on (@). </li>
</ul>
<p>They work in the same way. Here's a quick reference for the v-bind and v-on directives: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Shorthand for v-on</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Now let's see what dynamic arguments are and how they work.</p>
<h3 id="heading-dynamic-arguments-in-vue">Dynamic arguments in Vue</h3>
<p>Directives have been able to have dynamic arguments since Vue 2.6.0. You can use a JavaScript expression in the directive argument if you wrap it inside square brackets.</p>
<p>But there are some restrictions:</p>
<ul>
<li>expressions should evaluate to a string</li>
<li>spaces and quotes are invalid</li>
</ul>
<p>Let's see a practical example</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Visit my Website<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Inside your data object you can define the directive arguments as if they were properties, where the property value is the name of your HTML attribute like <code>hef</code> in the following example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">attribute_name</span>: <span class="hljs-string">'href'</span>,
        <span class="hljs-attr">url</span>: <span class="hljs-string">'https://fabiopacifici.com'</span>
    }
})
</code></pre>
<p>The code above renders the attribute name <code>href</code> and its value dynamically when you bind it using the v-bind directive.</p>
<p>The result will be this:  </p>
<pre><code class="lang-htm">&lt;a href="https://fabiopacifici.com"&gt;Visit my Website&lt;/a&gt;
</code></pre>
<h3 id="heading-dynamic-events-in-vue">Dynamic events in Vue</h3>
<p>You can apply the same concept to event directives like v-on. This directive does the job of the JavaScript event listener. </p>
<p>v-on accepts an argument like click, for example <code>v-on:click="doSomething"</code>.</p>
<p>To apply the concept of dynamicity, let's create a v-on directive and use the square brackets after it to specify a dynamic event.</p>
<p>Inside the index.html file you will place the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">event_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break down the code above. </p>
<p>First you have your root element, the <code>div</code> with an <code>id</code> of <code>app</code>. Inside the root element you add an anchor tag <code>&lt;a&gt;Some link&lt;/a&gt;</code>. </p>
<p>The anchor tag has a <code>v-on</code> directive in it. After the directive you specify a dynamic argument <code>v-on:[event_name]</code> where <code>event_name</code> will be a property inside your Vue instance that you can change as you need. </p>
<p>The v-on directive works like any event listener, so between quotes you need to specify the name of the function that you want to run when the event is triggered, therefore <code>runFunction</code>.</p>
<p>Now, inside your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">event_name</span>: <span class="hljs-string">"click"</span>
    },
    <span class="hljs-attr">methods</span>: {
        runFunction() {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"test click function"</span>);
        }
    }
})
</code></pre>
<p>Let's review what the code above does. </p>
<p>First, you create the Vue instance. Then you add the <code>event_name</code> property inside the data object and you assign to it a value of <code>click</code>. This is the event you will listen for.</p>
<p>Finally, we said that the v-on directive runs a function when the event is triggered, therefore you need to write a method inside your Vue instance. So inside the methods object, create a new function called <code>runFunction</code> that will simply output a message inside the console. </p>
<p>The power of dynamic events is clear when you replace the value of the <code>event_name</code> property with a different event name.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/LICvNmhsTEs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-methods-in-vue">Methods in Vue</h2>
<p>So far we've learned how to bind data using the v-bind directive inside your template. In the next section, you will learn more directives – but before diving into that, let's quickly talk about how to store your functions. </p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/5-Methods/">Repository here</a> </p>
<p>And you can see the video version of this section on <a target="_blank" href="https://youtu.be/dESmaEvkZ2I">YouTube</a> if you want to review what you learn.</p>
<p>Since you are working in a big object, the Vue instance function will take the name of the methods. And as you might guess, the Options object has a property called <code>methods</code> where you can store your functions as you do for your data. </p>
<p>Inside your Vue instance, define a method that you can call anything you like – just remember to use a naming convention that clearly describes your code.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">firstName</span>: <span class="hljs-string">"Fabio"</span>,
        <span class="hljs-attr">lastName</span>: <span class="hljs-string">"Pacific"</span> 
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// es6 syntax</span>
        getFullName(){
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.firstName + <span class="hljs-string">" "</span> + <span class="hljs-built_in">this</span>.lastName;
        }
        <span class="hljs-comment">// es5 syntax</span>
    <span class="hljs-comment">/* getFullName: function(){

        } */</span>
    }
});
</code></pre>
<p>In the code above, you created a method inside the methods object. You called it <code>getFullName</code>. Inside a method, you have access to the <code>this</code> keyword that refers to the object instance, so you can use it to access from a method the properties stored in the data object.</p>
<p>When you call the method <code>getFullName</code> the method will return a single string that contains both the first and the last name. </p>
<p>Now inside your HTML file, you can simply call the method as you did when you needed to access properties in the data object <code>{{ getFullName() }}</code> </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ getFullName() }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that you know how to create a method and where to put it in the Vue instance, let's move forward and learn more about directives.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/dESmaEvkZ2I" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals in Vue (v-if/v-else-if/v-else/v-show)</h2>
<p>Now it's time to learn more about directives. We will start by looking at how conditionals work in VueJS. The first directive of this section is the <code>v-if</code>, which allows you to render blocks of code based on a certain condition. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/6-Conditionals/]">Repository here</a>.</p>
<p>And you can click to view the video on <a target="_blank" href="https://youtu.be/VNaCsloA1ZU">YouTube here</a> or use it to review at the end of the section.</p>
<p>Like the <code>if-else</code> statements in vanilla JavaScript, the v-if will check if the returned value of a conditional expression evaluates to true. If so it will render the HTML element and everything you place inside it. </p>
<p>Since it's a directive, it works on a single element (HTML tag). If you want to extend its behaviour on multiple elements, then you need to wrap them inside a <code>&lt;template&gt;</code> tag.</p>
<p>The v-if directive works in the same way as the v-bind directive works: it has access to the properties in the data object and accepts an expression between its quotes. </p>
<p>If the returned value of the expression or the value of the data property you use evaluates to <code>true</code>, then the directive renders the HTML element. Otherwise it doesn't.</p>
<p>Of course, you can check for multiple conditions and end up rendering an element if none of these evaluates to true. You do that using v-if together with the v-else-if and v-else directives.</p>
<p>Let's see a simple example and write some code inside your main.js file to show or hide an element.</p>
<p>The first thing to note is that if you have a property that returns a boolean value, it is enough to use it inside the v-if directive to show/hide an element, like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"showTitle"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>And a vue instance with a showTitle property set to true.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">showTitle</span>: <span class="hljs-literal">true</span>,
    }
})
</code></pre>
<p>In such a case, you are saying show the title property only if the value of <code>showTitle</code> is <code>true</code>. If you change it to false, the title won't show.</p>
<p>You can put a simple expression inside the quotes of a v-if directive that once computed evaluates to a boolean.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt;= 18"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside your main.js file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: <span class="hljs-number">18</span>,
    }
})
</code></pre>
<p>In the code above we wrote an expression on the v-if directive that checks if the <code>age</code> property is greater than or equal to 18. If the result is true then the <code>h2</code> will be shown onto the page.</p>
<p>Now let's move to a more complex example and add another condition using the v-else-if.</p>
<h4 id="heading-v-ifv-else-if">v-if/v-else-if</h4>
<p>In the following example, you will first create a v-if condition similar to the one above – but this time you'll check if the user is over 18 but under 21 using the <code>&amp;&amp;</code> operator. </p>
<p>If true, then you'll show the time with an additional note. If false, and the user is over 21, then we will simply show the title of the movie. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside the Vue instance you could have an <code>age</code> property. But to make your simple program dynamic, you can instead use a prompt to ask the user their age. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>So the code here first asks the user their age, then stores the result as a number inside the variable <code>userAge</code>. </p>
<p>You'll later use the <code>userAge</code> variable inside the data object to assign a value to the <code>age</code> property so that based on its value you will render one element or the other.</p>
<p>Let's move forward and use the v-else directive to show a different message in case the user is under 18.</p>
<h4 id="heading-v-else-directive">v-else directive:</h4>
<p>The <code>v-else</code> directive works differently. You don't have to pass it anything. It simply enters into action when none of the previous conditions evaluate to a true value.</p>
<p>So the new HTML element is fairly simple:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-else</span>&gt;</span> Sorry You are too young to see this movie<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we have a <code>p</code> tag with a v-else directive attached. As you can see, it looks like an attribute without values (like the disabled or required HTML attributes).</p>
<p>Your JavaScript file has not changed.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>That's all you need to know about conditional rendering to be able to move forward with your first project. But if you want to learn more, here is the documentation: [https://vuejs.org/v2/guide/conditional.html]</p>
<p>You need to learn a few more things before you'll be able to build your first project, which is a simplified Twitter clone. The next topic is about Loops.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VNaCsloA1ZU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-loops-in-vue">Loops in Vue</h2>
<p>Let's go back to the previous example and learn how to use the v-for directive to output each project of the array onto the page. </p>
<p>You can click to view the Repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/7-loops/">here</a></p>
<p>And you can click to view the YouTube-Video <a target="_blank" href="https://youtu.be/aViHg80-7Bs">here</a>, or you can find it at the end of this section to review what you've learned.</p>
<p>For our next task, it would be useful if we could use a loop, and the v-for directive is here to help.</p>
<p>Its syntax doesn't have much in common with a classic <code>for</code> loop in JavaScript, but rather with a Python <code>for in</code> loop or with the <code>for in</code> JavaScript loop used to iterate over objects.</p>
<p>With this directive, you specify the elements of the array and the single element between quotes using the syntax <code>project in projects</code>. Here, projects are the property inside the data object that contains an array of objects, and project is the single element of the array. </p>
<p>You can call this as you like, but keep in mind that what follows the <code>in</code> keyword must be an iterable from your data object while what comes before can be anything you like to refer to each element of the iterable. </p>
<p>In your case, project seems the most appropriate choice since you have an array of projects.</p>
<p>Your JavaScript file will look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>Now inside the HTML file, let's use the v-for to render the title of each project.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>{{project.title}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the code above, you used <code>{{name}} {{title}}</code> to render the main title for your portfolio. Then you used the v-for directive and specified inside the quests that you want to assign each element of the iteration to a project variable <code>v-for="project in projects"</code>.</p>
<p>Now on each iteration, the <code>project</code> variable holds an object from which you can retrieve its properties using dot notation like so: <code>{{ project.title }}</code>.</p>
<p>One thing to note is that the v-for directive also gives you access to the index of the element at each iteration. You can store it in a variable as you did with the single element you called project. </p>
<p>To do that, you need to wrap them between parentheses and separate the element and its index with a comma, like so <code>v-for="(project, index) in projects"</code>. </p>
<p>Also, note that when working with objects Vue can show an alert to inform you that the use of a key is recommended. This means that it expects a key to identify each element when it's rendered. </p>
<p>You can do this using the <code>key</code> attribute and bind it, for instance to an id property on the object or to another different property, like so</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"project.title"</span>&gt;</span>{{project.title}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here you used the v-bind shorthand directive to bind the key attribute to the project.id property if it exists or to another property if not. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>v-for can also be used to iterate over objects. In such a case, you have access to the value, the key, and also the index like so <code>v-for="(value, key, index) in object"</code> where 'object' is a property in the data object.</p>
<p>If you want to learn more, visit the documentation here: [https://vuejs.org/v2/guide/list.html]</p>
<p>Let's now move on to another important feature of Vue: how to handle user inputs and events. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aViHg80-7Bs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to Handle User Input with Event Handling (v-on) in Vue</h2>
<p>To make the application react to a user's input, Vue provides a straightforward directive called <code>v-on</code>. This is one of the directives that accepts arguments, similar to the v-bind directive. </p>
<p>With such a directive, it's easy to listen for events triggered by a user.</p>
<p>The v-on directive lets you run a function that executes a block of code when the user performs an action, like when they click on a button, hover on an element, or press a specific key on the keyboard.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/8-user-inputs-events-handling/">Repository here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/9_U1eagqOJY">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-v-on-syntax-and-events">V-on Syntax and Events</h3>
<p>There are two types of syntax we can use, the long-form or the short. They're equivalent, so pick the one you prefer. What follows is just a representation of the syntax, and I'll explain it in detail in a minute.</p>
<p>Long syntax: <code>v-on:EventName='doSomething'</code>
Shot syntax: <code>@EventName='doSomething'</code></p>
<p>There are many events you can listen to, such as: </p>
<ul>
<li>click</li>
<li>submit</li>
<li>mouseover</li>
<li>mouseenter</li>
<li>mouseleave</li>
<li>keyup</li>
<li>keydown</li>
<li>keypress</li>
</ul>
<p>But you can also create custom events (which you will see when you reach the components section).</p>
<p>Let's pick the long-form syntax: <code>v-on:EventName='doSomething</code>. I'll explain it more now.</p>
<p>First, you have the directive <code>v-on</code>. Then you have an argument that is the event name you want to listen for, like <code>click</code>. After that, the <code>doSomething</code> can be any method that you have defined inside the methods object of the Vue instance.</p>
<p>This method is like any other function that you define inside a JavaScript object. It can have parameters or not. If it has them, you can call the method and pass parameters to it as usual like this: <code>doSomething(param, param_2, param3)</code>. </p>
<p>You can have something like this <code>&lt;div v-on:click="likeProject"&gt;Like&lt;/div&gt;</code> and when the user clicks on this element, it will trigger a method and run some code to increase a like counter inside a project.</p>
<p>Let's first create the HTML you need for that:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(project,index) in projects"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        {{project.likes}}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the code here, first you'll use the v-for directive to loop over the array of projects. Note that you should use the syntax <code>(project, index) in projects</code> because you will need to pass the index to the like method that you defined earlier. </p>
<p>After that, you output some data onto the page (like the project name in uppercase letters) then the description, and a <code>div</code> tag with an icon for the likes (remember to add font awesome to get the icon). </p>
<p>On the heart icon, add the directive v-on using the short syntax <code>@click="likeProject(index)"</code> between quotes that you used to invoke your <code>likeProject(index)</code> method. Then pass to it the index as a parameter so you can find the current project the user clicked on. </p>
<p>Finally, you'll render the likes onto the page for the current project using the <code>{{project.likes}}</code> syntax.</p>
<p>Now it's time to go in the Vue instance and write your method.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
            likeProject(index){
                <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
                project.likes++
                <span class="hljs-built_in">console</span>.log(project.likes)
            }

    }
});
</code></pre>
<p>As I said earlier, you needed to define a method to call when the user clicks on a link. So you create the <code>likeProject</code> method, which accepts a parameter that will be the index of the element the user clicked on. </p>
<p>You can then add a likes property inside your projects array and access it for the current project to increment its value every time the user clicks on your link.</p>
<h3 id="heading-how-to-access-the-original-event">How to access the original event</h3>
<p>If for any reason you need to access the original DOM event, you could have used the special <code>$event</code> variable inside the method like this on the v-on directive: <code>doSomething(param1, param2, $event)</code>. Let's see an example of that now.</p>
<p>You need to add the special variable in the method call on your v-on directive like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index, $event)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
</code></pre>
<p>Then you can access the original event inside your method like so: </p>
<pre><code class="lang-js">likeProject(index, event){
    <span class="hljs-built_in">console</span>.log(event); <span class="hljs-comment">// get the original event</span>
    <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
    project.likes++
    <span class="hljs-built_in">console</span>.log(project.likes)
}
</code></pre>
<p>Now that you know how the v-on directive works, let's improve our Likes example and put something more in it. We will use key modifiers in the next example, so let's quickly see what they are and what you can do with them.</p>
<h3 id="heading-event-modifiers-in-vue">Event Modifiers in Vue</h3>
<p>With events, Vue provides access to a number of Event modifiers. They are divided into 4 main groups. You can add these modifiers to a directive to change the way your event behaves. They are like postfix and you can chain them using dot notation. </p>
<p>Below there is a quick reference.</p>
<p>Categories:</p>
<ul>
<li>event modifiers</li>
<li>key modifiers</li>
<li>system modifiers keys</li>
<li>mouse buttons modifiers</li>
</ul>
<p>Event Modifiers:
.stop
.prevent 
.capture
.self
.once
.passive</p>
<p>Key Modifiers:
You can add these modifiers to the @keyup listener to listen for when these keys are pressed or use them as a combination with the @click event to listen for a click+space, for example. <code>@click.enter="doSomething"</code></p>
<p>.enter
.tab
.delete (captures both “Delete” and “Backspace” keys)
.esc
.space
.up
.down
.left
.right</p>
<p>System modifiers:
With these modifiers, you can trigger mouse or keyboard event listeners when the corresponding key is pressed.
.ctrl 
.alt
.shift
.meta
.exact (allows control of the exact combination of system modifiers needed to trigger an event)</p>
<p>Mouse buttons modifiers:
These modifiers allow you to trigger a mouse event listener if the corresponding mouse button is clicked. </p>
<p>.left
.right
.middle</p>
<p>If you want to learn more, read the docs <a target="_blank" href="https://vuejs.org/v2/guide/events.html#Event-Modifiers">here</a>.</p>
<h3 id="heading-how-to-like-a-project-with-key-modifiers">How to like a project with key modifiers</h3>
<p>In the previous example, you used the v-on:click directive to trigger a mouse event listener that aimed to simulate a like on a project. </p>
<p>But the user was able to add as many likes they wanted by clicking on the icon. </p>
<p>In the next example, you will do things a bit differently.</p>
<ul>
<li>First, you will prevent the user from adding more than one like to each project,</li>
<li>Then you will let the user remove a like </li>
<li>Finally, you will keep the likes on the page even after the user refreshes the page.</li>
</ul>
<p>Let's get started. This time you will use mouse button modifiers to listen for clicks. The left mouse button click will trigger the add like behaviour and the right mouse button click will trigger the remove behaviour.</p>
<p>Inside your HTML file: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like 
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                    @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                    @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
               {{project.likes}}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>


<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the code above you have taken what you had before and simply added a left mouse button key modifier to the click event <code>@click.left</code>. Then you invoked the <code>addLike</code> method. This will make your project like counter increase by one as we have seen before. </p>
<p>Then you added another event listener to the same element, but this time you used the <code>.right</code> mouse button key modifier to listen when the user clicks on our icon using the right button <code>@click.right="removeLike()"</code>. </p>
<p>In the remove like method, you have also passed the special variable $event so that you can use the original event later in your method to prevent its default behaviour and open the contextual menu. </p>
<p>But we said earlier that you can also chain key modifiers and indeed there is a <code>.prevent</code> key modifier that you can use here instead of the <code>$event</code> variable. You could do the same like this: <code>@click.right.prevent="removeLike(project)"</code> </p>
<p>Let's see how to structure your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
    <span class="hljs-attr">methods</span>: {
        addLike(project){
           <span class="hljs-built_in">console</span>.log(project)  
        },
        removeLike(project, event){
            <span class="hljs-built_in">console</span>.log(project)
            <span class="hljs-built_in">console</span>.log(event)
        }
    }

});
</code></pre>
<p>So in the data object, you have a <code>projects</code> property that is an array of objects. Each object has a likes property that you will increment or decrement depending on what mouse button the user clicks.</p>
<p>Inside the <code>methods</code> object, you created the two methods you referenced in your v-on directives <code>addLike()</code> and <code>removeLike()</code>. For now, you are only logging to the console the project parameter value and the event value. You will implement the logic in a minute.</p>
<p>Let's start with the add likes method – it could look like this:</p>
<pre><code class="lang-js">addLike(project){
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes++;
        <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
    }              
}
</code></pre>
<p>There are a few things going on here. In the first line, you're storing the project title inside the <code>projectTitle</code> variable. Then, you said you wanted data to persist if you refresh the page so you are using the <code>localStorage</code> API to store information inside the client browser. </p>
<p>You increment the likes count by one, but you do that depending on a value inside the local storage. </p>
<p>You can do this by first checking to see if there is a key in the <code>localStorage</code> matching your project title, <code>if(!localStorage.getItem(projectTitle))</code>. </p>
<p>If this evaluates to false, then you will run the code inside the if block and first increment the likes <code>project.likes++</code>. </p>
<p>Second, use the <code>.setItem()</code> method of the local storage API to set a key-value pair with the project title as the key and a boolean value as its value <code>localStorage.setItem(projectTitle, true)</code>.</p>
<p>To put an item in the local storage, you'll use <code>localStorage.setItem()</code>. The set item method accepts a key-value pair. Your key will be the title you saved in the variable <code>projectTitle</code> and the value will the boolean value <code>true</code>.</p>
<p>Now let's see if this works. </p>
<pre><code class="lang-js">removeLike(project, event){
    event.preventDefault(); <span class="hljs-comment">// This can be omitted if we use the prevent key modifier</span>
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes--;
        <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
    }
}
</code></pre>
<p>This function does the opposite of the previous. When the user clicks their right mouse button, the method <code>removeLikes()</code> is executed and you do the following:</p>
<p>First things first, you need to prevent its default behaviour. Otherwise when the user right-clicks on the icon, the contextual menu will pop up and we don't want that. So, you'll use the <code>event.preventDefault()</code> method on the original event that is represented by the <code>event</code> parameter on your method. </p>
<p>Alternatively, you can omit this if you use the prevent key modifier in the v-on directive <code>@click.right.prevent="removeLike(project)</code>.</p>
<p>The next step is to grab the project title. Since you also passed a parameter to the method to represent the current project object <code>removeLike(project, event)</code>, you can store the project title in a variable <code>projectTitle</code>.</p>
<p>Then you need to make a couple of checks. First, you want to decrement the likes only if its value is greater than zero. Then you want to make sure the project title is in the local storage as a key with a value. </p>
<p>So, in your condition, you have done both checks <code>if(project.likes &gt; 0 &amp;&amp; localStorage.getItem(projectTitle))</code>. Now, if both conditions evaluate to true, the code inside the if block can run. </p>
<p>First you remove the like by decrementing its value <code>project.likes--</code>. Then you remove the project title from the local storage using the <code>removeItem</code> method and pass to it the key you want to remove (which is the project title <code>localStorage.removeItem(projectTitle)</code>).   </p>
<p>To put it all together, you should now have the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
        addLike(project)
        {
            <span class="hljs-comment">//console.log(project, "like");</span>
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-comment">// check if the current project is not in the local storage</span>
            <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
                <span class="hljs-comment">// set the item in the storage and increase the likes counter</span>
                project.likes++;
                <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
            }

        },
        removeLike(project){ 
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-built_in">console</span>.log(project, <span class="hljs-string">"dislike"</span>);  
            <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">Boolean</span>(<span class="hljs-built_in">localStorage</span>.getItem(projectTitle))) {
                project.likes--;
                <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
            }

        }
    },
    mounted(){
        <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span> {
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(project.title) !== <span class="hljs-literal">null</span>) {
                project.likes = <span class="hljs-number">1</span>; 
            }
        });
    }

});
</code></pre>
<p>To make the code work, you also need to add a life cycle hook called mounted. This will let you run code when the root element is mounted on the Vue instance. With it, you can check if the localStorage has a key corresponding to your project title and if so, update the value of the likes counter.</p>
<p>And your HTML is still the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Like 
            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
            {{project.likes}}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Remember that you can get rid of the <code>$event</code> variable passed to the <code>removeLike</code> method by using the event key modifier like so: <code>@click.right.prevent="removeLike(project)"</code></p>
<p>You have learned a lot so far! And now that you have seen key modifiers in action, we can move forward to the next topic: two-way model binding and the v-model directive. Then we'll start building our Twitter clone. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9_U1eagqOJY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-two-way-model-binding-v-model-in-vue">Two Way Model Binding (v-model) in Vue</h2>
<p>All right, so far we have seen how to bind properties from the data object to our HTML tags and inside attributes, 
how to loop over a sequence of elements and, how to display conditionally elements onto our template with conditionals. </p>
<p>We have covered how to define methods inside the methods object so that we can perform more complex operations on our data and, 
we have learned how to work with events using the v-on directive.</p>
<p>In the next section, we will look at how Vue opens a two-way communication channel between a form's input and, a property defined
inside the data object. Then we will use this knowledge to build our first project together.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/9-two-way-binding/">Repositoy here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/pBUXTUvDRCo">YouTube here</a> or at the end of the section if you want to review what you've learned.</p>
<h3 id="heading-how-does-the-v-model-directive-work">How does the v-model directive work?</h3>
<p>The v-model is another Vue directive. You can use it out of the box, and it's useful to simplify the way an input tag can communicate 
with a Vue instance's property in the data object. </p>
<p>It works like all the other directives. The main difference is that when it's implemented, your application will listen for changes 
inside the input with this v-model directive and update the attached property's value immediately inside the data object and vice-versa. </p>
<p>It is effectively a two-way communication channel between your template and the Vue instance. It's the Vue way to interact with user input and simplify your life as a developer.</p>
<p>Let's look at a straightforward example.</p>
<h3 id="heading-how-to-use-the-v-model-on-an-input-tag">How to use the v-model on an input tag</h3>
<p>First, you need an input tag inside the element you defined as the root element in the Vue instance, the div with an id of <code>app</code>. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the HTML you have an input tag, which has the v-model directive attached to it as an HTML attribute. Anything between quotes is computed as a JavaScript expression, so you write the name of a property <code>tweet</code> that you will create inside the Vue data object.</p>
<p>So let's do it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>
    }
});
</code></pre>
<p>So now you have a Vue instance and, in it, you have a data object with a tweet property that has an empty string as its value. </p>
<p>If you open the console and inspect the Vue element, you can see the two-way data binding in action. </p>
<p>By changing the value of the tweet property, you will immediately update the value inside the input tag and vice-versa. </p>
<p>You have this <code>tweet</code> property in the data object and you already know how to render its content onto the page. So now you can update your markup and add a paragraph under the input tag to see the value dynamically changing while you type.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{tweet}}<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>How cool is that? Now you can see the tweet property changing in real-time as you type. </p>
<p>That's the two-way data binding. If you change the content of the tweet property directly it will be reflected in your template, too.</p>
<p>If you want to learn more, make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/forms.html">Documentation</a> too.</p>
<h3 id="heading-how-to-build-a-tweet-box">How to build a tweet box</h3>
<p>Now, let's raise the bar a little and build something together.</p>
<ul>
<li>We will create a simple <code>textarea</code> with a submit button, </li>
<li>We will display the number of characters the user has left while they are typing so that they can submit the form without exceeding the max number of characters allowed. </li>
<li>Like in a tweet, the maximum number of characters will be 200.</li>
</ul>
<h4 id="heading-how-to-define-the-initial-markup">How to define the initial markup</h4>
<p>Now you need to define a markup. So inside our index.html file, write the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>First you have first created your root element for the Vue instance so that it can monitor the markup and do its magic.</p>
<p>Then you created a form tag with an event listener using the directive v-on, which listens for the submit event and runs a function <code>submitData</code> that you still have to create.</p>
<p>You also added the <code>.prevent</code> modifier so that the page doesn't refresh when you submit the form.</p>
<h4 id="heading-how-to-define-the-vue-instance-and-methods">How to define the Vue instance and methods</h4>
<p>Let's define our Vue instance and create the <code>submitData</code> method so that you can use it later when you need it.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-comment">// data object props here</span>
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
             <span class="hljs-comment">// Code here</span>
        }
    },

});
</code></pre>
<h4 id="heading-how-to-add-a-text-area-and-a-submit-button">How to add a text area and a submit button</h4>
<p>Now back to the HTML: let's add the text area inside your form. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the form tag, you create a label and a <code>textarea</code> for your tweet box. 
On the <code>textarea</code> you use the directive v-model to bind the <code>textarea</code> value to a <code>tweet</code> property and vice-versa. So now when one changes the other changes, too. </p>
<p>NOTE: The v-model directive is used on form elements like inputs, text areas, check box, and more.</p>
<p>After the <code>textarea</code> you put a button of type submit so that when a user clicks it the form's data are sent to your application's <code>submitData()</code> method and you can process them.</p>
<h4 id="heading-how-to-add-properties-to-the-vue-instance">How to add properties to the Vue instance</h4>
<p>Now inside your JavaScript file, you need to create the tweet property in the data object and do something with this information so that you can later show a list of tweets sent. </p>
<p>We also said that we want to limit the characters to 200 and show an error when they're in excess.</p>
<p>So let's add a few more properties here like:</p>
<ul>
<li><code>tweet</code> for the current tweet message that the user inputs in the text area</li>
<li><code>tweets</code> for the list of tweets</li>
<li><code>max_length</code> for the characters limit</li>
</ul>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-comment">/* Handle the tweet */</span>
        }
    },

});
</code></pre>
<p>So now with the tweets property as an array and using the two-day binding between the tweet property and the <code>textarea</code>, you can push all tweets inside the <code>tweets</code> array when the user submits the form by triggering the 
<code>submitData</code> method.</p>
<h4 id="heading-how-to-implement-the-character-counter">How to implement the character counter</h4>
<p>Before implementing the <code>submitData</code> method, you can show a character counter while the user types in the textarea. </p>
<p>Let's implement this feature so the user knows if they can submit the tweet or not.</p>
<p>Back in the HTML file, you can add a div with a couple of span elements and use a v-if directive to check the length of the character. It'll show the counter while the user is within the characters limit, otherwise it'll show an error message.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>        
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show character limits here --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The code above uses two-way data binding between the tweet property and the <code>textarea</code> to find out if the user has reached the character limit that you defined as the <code>max_lenght</code> property. </p>
<p>Since the tweet property is connected to the <code>textarea</code>, you can use the <code>v-if</code> directive combined with the <code>tweet.length</code> and the <code>max_length</code> properties to make the comparison. </p>
<p>Now, every time the user types something in the <code>textarea</code>, the string saved in the <code>tweet</code> property increases by one character. Then you can use the <code>.length</code> property to see how long the whole string is and compare it against your <code>max_length</code> property.</p>
<p>You use the directive  <code>v-if="tweet.length &lt;= max_length"</code> to make your comparison. When this comparison returns true, the user will see the span tag with its content, the counter. </p>
<p>Inside the span tag, you used the moustache syntax to show the user the current length of the property <code>tweet</code> and the character limit.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<p>After the <code>v-if</code> directive, a <code>v-else</code> directive handles the error message that you show to the user when there are no characters left to use. </p>
<p>Here the content of the span element shows a message that tells how many characters there are in excess by subtracting the tweet length from the <code>max_lenght</code> property.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<h4 id="heading-how-to-submit-the-form">How to submit the form</h4>
<p>All that's left is to add the tweet to the list of tweets and show them on the page when the user submits the form.</p>
<p>Let's complete the <code>submitData</code> method so that every time it's executed it pushes a new object to the tweets array. </p>
<p>Inside the methods object the <code>submitData</code> methods now looks like this:</p>
<pre><code class="lang-js"> submitData(){
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
        <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
        <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
    } 
}
</code></pre>
<p>The method above first checks if the length of the <code>tweet</code> property is less than or equal to the <code>max_length</code> property. If the condition evaluates to true, then you can add the <code>tweet</code> content to the array using the <code>unshift</code> method (which adds it to the beginning of the array).</p>
<p>Finally, you need to clear the value of the <code>tweet</code> property. You can do this by assigning to it an empty string again.</p>
<p>NOTE that since you are inside a method, you need to use the <code>this</code> keyword to grab properties and eventually methods inside the Vue instance.</p>
<h4 id="heading-how-to-show-a-list-of-tweets">How to show a list of tweets</h4>
<p>Now, you can also show a list of tweets in your template. </p>
<p>To do that you will use a <code>v-for</code> directive and loop over the <code>tweets</code> array to show each tweet.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<h3 id="heading-put-it-all-together">Put it all together</h3>
<p>The final code now looks like this:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJs v-model<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Fontawesome CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you wanna tweet about today<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Tweet form --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Alert the user  --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span>
                {{ ` Max: ${tweet.length} of ${max_length} characters` }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">v-else</span>&gt;</span> {{ `Max char limit reached! excess characters: ${max_length -
                tweet.length} ` }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Tweets message --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"tweet in tweets"</span>&gt;</span>
                {{tweet}}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Our final javascript file </p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>        
    }, 
    <span class="hljs-attr">methods</span>: {
        submitData(){
            <span class="hljs-comment">// Handle the tweet submission</span>
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length){
                <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
            }
        }
    }
})
</code></pre>
<h3 id="heading-improvements-you-can-make">Improvements you can make</h3>
<p>If you take this bit of code from your index.html file, there is something you can do to clean up our code... </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>To clean this template file, we can follow two approaches that are exactly the same, except that one is cached and the other one isn't. </p>
<ul>
<li>Computed Properties (cached)</li>
<li>Methods (not cached)</li>
</ul>
<p>In the next section, we will learn what computed properties are and how they differ from methods.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pBUXTUvDRCo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-computed-properties-and-methods">Computed Properties and methods</h2>
<p>You should use computed properties instead of in-template expressions for complex logic that has the scope of changing the presentation of our data, not the data itself. </p>
<p>If we need to change the data, then you should use methods instead. Computed properties are cached based on their dependencies, meaning that it will re-evaluate only when its dependencies have changed. </p>
<p>With computed properties, the result of the previously run function is returned if the dependencies have not changed. </p>
<p>You can view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/10-computed-properties/">repository here</a> and watch the video on <a target="_blank" href="https://youtu.be/VxFT6cgTHhw">YouTube here</a>. The video is also listed at the end of this section so you can review what you've learned.</p>
<p>In the following example, we will use computed properties but we could also use methods. Generally speaking, we use computed properties when we have an expensive operation that we want to execute and cache so that the next time we don't have to run it again unless something has changed.  </p>
<p>Let's implement a computed property for the following messages. Our HTML file will now change from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>to this much cleaner version:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>We have replaced the contents of both spans with two new properties that will be placed as methods inside our computed object.</p>
<p>Now inside our Vue instance, we will create a new object called <code>computed</code> where we will define two methods that will return the messages we had before. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>The first method <code>maxCharsText</code> returns exactly the same string we had before inside our HTML file. The only difference is that we are using the keyword <code>this</code> to reference the properties we needed to grab inside the Vue instance <code>this.tweet.length</code> and <code>this.max_length</code>. </p>
<p>The second method works in the exact same way and it also uses the keyword <code>this</code> to pick the properties defined in the Vue instance <code>this.max_length</code> and <code>this.tweet.length</code>.</p>
<h3 id="heading-all-together">All together</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Next<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>JavaScript file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>If we want to use methods instead of computed properties, we can simply move both methods from the <code>computed</code> object inside the <code>methods</code> object and invoke them with parenthesis inside our HTML file like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText() }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage() }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Just remember that computed properties are cached while methods are not. 
If you want to learn more make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/computed.html">documentation</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VxFT6cgTHhw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-create-a-simple-twitter-clone">How to Create a Simple Twitter Clone</h2>
<p>Now, let's put together everything we've learned so far and and build our very first project. It will be a minimalist and simplified twitter-like web application. </p>
<p>We want to create a simple application that has some kind of registration form, a box to add new tweets, and a section where we can show all tweets. We also want to be able to remove tweets.</p>
<p>All data must be persistent so that after the page is refreshed the list of tweets is still visible while the registration form will be hidden.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/v1j_bDDd6jI">YouTube here</a> or at the end of this section if you want to review.</p>
<p>You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/11-project-simple-twitter-cone/">here</a>.</p>
<h3 id="heading-define-our-tasks">Define our tasks</h3>
<p>Let's break this down into large tasks first. Then we will see what we need to do to complete each one. </p>
<ul>
<li>create a registration form</li>
<li>create a tweets box form</li>
<li>create a tweets section</li>
</ul>
<p>To do our project we need to research what tools we need to use to achieve our goals, so lets put them down:</p>
<ul>
<li>VueJS (application logic)</li>
<li>localStorage (make data persistent)</li>
<li>font awesome (icons)</li>
</ul>
<p>This application has no database so we are unable to record multiple users and their tweets. It's just a proof of concept, something we build to use our new knowledge.</p>
<h3 id="heading-create-the-project-structure">Create the project structure</h3>
<p>Now that we know what to do, let's start by creating our project structure and importing the tools we need to complete the first task, the registration form.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Simple tweetter clone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- CDN Fontawesome --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span> /&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Style sheet --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>

    <span class="hljs-comment">&lt;!-- Add a tweet --&gt;</span>

    <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Link our main.js file --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Now that our HTML file is ready, let's create the main.js file and create a Vue instance.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {

    },
    <span class="hljs-attr">methods</span>: {

    }

});
</code></pre>
<p>Finally, we need to create a style.css file that we will place for now in the root folder of our project. </p>
<p>We will use a CSS file that I have already written, and you can download it from <a target="_blank" href="https://bitbucket.org/fbhood/simple-tweet-app/src/master/style.css">here</a>.</p>
<p>OK, our basic structure is ready to go. Inside our HTML file, we have some comments that reflect our 3 main tasks: create a registration form, create an add tweet box and, show a list of tweets. </p>
<p>Let's start with the first task and simulate a registration form.</p>
<h3 id="heading-how-to-simulate-a-registration-form-html">How to simulate a registration form - HTML</h3>
<p>Inside the root element <code>&lt;div id="app"&gt;&lt;/div&gt;</code> we need to create a registration form with the following fields: name, email, password, and a submit button. The form is contained in a card so we will wrap everything in a div and assign to it a card class. </p>
<p>The form won't submit data to a server, but will simply simulate a registration and update some property in the data object of the Vue instance.</p>
<p>We will place the following code inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"16"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break this down. First, the form tag has a <code>v-on</code> directive with a <code>submit</code> argument so it will listen for a submit event. It also has an event modifier <code>.prevent</code> so when we hit the submit button the page doesn't refresh. </p>
<p>Inside the <code>v-on</code> directive there is a method called <code>registerAccount</code> that we need to create inside the methods of the Vue instance.</p>
<p>Inside the form, we have the three input fields: name, email, and password with labels. We wrapped each field inside a div with a class of <code>form_group</code>. Later we can replicate the style of the Twitter registration fields and show a character counter. </p>
<p>Each input field has a <code>v-model</code> directive that binds the input to its data property. </p>
<h3 id="heading-how-to-simulate-a-registration-form-vue">How to simulate a registration form - Vue</h3>
<p>Let's move on to the Vue instance to make the binding between the form and the data object properties.</p>
<p>Here we also need a place where we can store the details that the user submits so we will create another property for that. </p>
<p>Looking at the input fields, we also put a <code>maxlength</code> property on them, which is 25 for the name and email and 16 for the password. </p>
<p>Like we did previously when we learned about the v-model, we can create a property for these limits so that we can use it to show the user how many characters the user has left. </p>
<p>The Vue instance will look like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {}
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>
    },
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-comment">// add registration to localStorage</span>
            <span class="hljs-comment">// clear the registration fields</span>

        }
    }

});
</code></pre>
<p>Let's break this down. First inside the <code>data:{}</code> object, we defined an object to store and retrieve the <code>userData</code>. Then we added the properties <code>name</code>, <code>email</code>, and <code>password</code> to make the two-way data binding work. 
Finally we added the <code>max_length</code> and <code>max_pass_length</code> propeties.</p>
<h3 id="heading-how-to-show-input-character-counter">How to show input character counter</h3>
<p>OK, now that we have a binding between the input fields and our properties, we can show a character counter to the user while they're typing. </p>
<p>This is fairly simple – all there is to do is to show the length of each input property and compare it with the max length properties we have set in the Vue instance.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>So here we created a string using an in-template expression. We've shown the length of the <code>name</code> property and the value of the <code>max_length</code> so that while the user types we show something like this: 13/25. </p>
<p>We've also used a <code>v-bind</code> directive on the <code>maxlength</code> attribute so that its value is bound to the value of the property we defined in the Vue instance. So in case we want to change it we can do so in one place.</p>
<p>We will do the same for the other fields. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-add-the-logic-to-the-registeraccount-method">How to add the logic to the <code>registerAccount</code> method</h3>
<p>Now it's time to work on the form submission logic. We will simply populate the object stored in the property <code>userData</code> when the user submits the form. </p>
<p>Inside the <code>registerAccount</code> method we will add the details the user passes and build our object. </p>
<pre><code class="lang-js"> registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
            <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
            <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            <span class="hljs-comment">// add registration to localStorage</span>

            <span class="hljs-comment">// clear the registration fields</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }
</code></pre>
<p>Here we have taken the value of the properties name, email, and password and assigned them to properties we created in the <code>userData</code> object.</p>
<p>This seems fine mostly because we have put on our input fields the <code>required</code> attribute – but if we remove it we will be able to submit an empty form, and we don't want that. </p>
<p>So let's add a very basic form of validation to at least check if the user has typed something inside our form, otherwise we show an error. </p>
<p>To do this, we need to add an if block inside the method and also an error property to the data object. Our file now looks like this:</p>
<pre><code class="lang-js">

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,

    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) 
            {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Here in the <code>registerAccount</code> we've written a conditional that checks if the length of the name property is not empty, if the email property isn't empty, and if the password isn't empty  <code>this.name !== "" &amp;&amp; this.email !== "" &amp;&amp; this.password !== ""</code>. </p>
<p>If all these checks evaluate to a true value, then we run the code inside the block. Otherwise we run the code in the <code>else</code> block that updates the value of the <code>error</code> property that we will now use in our template to show the error message. </p>
<p>We also added a new property <code>usersID: 0,</code> that we used inside the if block to assign an id property to the <code>userData</code> object, just to make our application more realistic. But of course it is useless, as we will not have a database where we store all user details. We'll just store a single user inside their browser's local storage.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now our form is complete and we're also displaying an error message to the user if the required attributes are removed from our markup. </p>
<p>But our data do not persist and when the page is refreshed –everything's all gone. Let's tackle that using the localStorage API.</p>
<p>Inside our Vue instance, we need to set an item in the local storage. But we also need to save the entire <code>userData</code> object so that later we can use its data to display a message to the registered user.</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Add registration data to the local storage */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
<span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))
</code></pre>
<p>Here we use the <code>setItem</code> method of the Local Storage API to add an item to the local storage so that later we can use it to check if the user is registered or not. </p>
<p>Then we also need to store the entire <code>userData</code> object as a string. To do that we use the <code>JSON.stringify</code> method that will turn the object into a JSON string that can be saved in the localStorage.</p>
<p>Our JS file is now as follows:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span>  &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
        <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Now when the user visits our application page, we need to check inside the browser's local storage to see if there is a key called <code>simple_tweet_registered</code>. If there is, we can assume that the user is registered and we can show the next section, the tweet box. Otherwise, we show the registration form. </p>
<p>We will do that by creating a <code>registered: false</code> property in the data object and use it to display or hide the registration form.</p>
<pre><code class="lang-js">
<span class="hljs-attr">data</span>: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,      
}
</code></pre>
<p>Wrap the form around a div with a directive <code>v-if="!registered"</code> like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
    // here goes the form
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span> Tweetbox <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our final HTML file now looks like this:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome username_here write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now to make this work, we will use the lifecycle hook we created which lets us inject our code when the Vue instance has been created. This is because we want to check this before mounting our root element. </p>
<p>So let's add a life cycle hook to the Vue instance. We will check if our key is there, and if so we will update the value of the <code>registered</code> property to <code>true</code>. </p>
<p>We have also stored the full <code>userData</code> object in the local Storage so we can use it to repopulate the <code>userData</code> object when the page is refreshed with the details the user submitted.</p>
<pre><code class="lang-js">created(){
    <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
        <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
    }
    <span class="hljs-comment">// repopulate the userData object</span>
     <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

}
</code></pre>
<p>To turn a JSON string back into an object, we can use the <code>JSON.parse</code> method.</p>
<p>Now it's all set for the next task – show a tweet form to the user after registration.</p>
<p>Our code so far looks like this:</p>
<p>The main.js file:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>And the HTML inside the <code>app</code> element:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here in the HTML, since we used the <code>v-else</code> on the add tweet section and used the local storage to retrieve the data submitted by the user, we can use an in-template expression to grab the user name so that we can output a welcome message. </p>
<p>In the next section, we will create a tweet box form so that after the registration the user can write a tweet.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/v1j_bDDd6jI" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-how-to-create-a-tweet-box-form-html">How to create a tweet box form - HTML</h3>
<p>Now it's time to build our add tweet form. We did something very similar earlier in this article, but now we will need to store and make the data persistent. This lets us show a list of tweets even when the page refreshes.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                Send your tweet
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This is nothing new to us now. Inside the <code>tweetBox</code> element we add a form with the usual v-on directive and a method  <code>sendTweet</code> that we will need to define inside the methods object. This will take the tweet and save it somewhere, maybe in a property in the data object.</p>
<p>Inside the form, there is a <code>textarea</code> that has a <code>v-model</code> directive that binds it to a <code>tweetMsg</code> property that we need to create.</p>
<p>Finally, a submit button.</p>
<p>We also have a span inside the tweet label that shows a character counter to the user as we did before in the registration form. </p>
<p>Here we have a new property <code>max_tweet</code> that is used to show the limit and the <code>tweetMsg.length</code> is used to show the current number of the characters inserted.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/xFwfrIciFt0">YouTube here</a> if you want to review what you've learned.</p>
<h3 id="heading-create-a-tweets-box-form-vue">Create a tweets box form - Vue</h3>
<p>Let's go to the Vue instance and add the properties and the <code>sendTweet</code> methods.</p>
<p>Our data object now has three more properties, the <code>max_tweet</code> set to <code>200</code>, the <code>tweetMsg</code> that binds to the <code>textarea</code>, and a <code>tweets</code> array that we will use to store all tweets the user sends.</p>
<pre><code class="lang-js">data: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>, <span class="hljs-comment">// max tweets lenght</span>
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>, <span class="hljs-comment">// current tweet</span>
    <span class="hljs-attr">tweets</span>: [] <span class="hljs-comment">// list of tweets</span>
}
</code></pre>
<p>Inside the methods, we have a new method that will be invoked by the v-on directive when the form is submitted:</p>
<pre><code class="lang-js">
sendTweet(){
    <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
    <span class="hljs-built_in">this</span>.tweets.unshift(
        {
            <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
            <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
        }

    );
    <span class="hljs-comment">/* Empty the tweetMsg property */</span>
    <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
    <span class="hljs-comment">//console.log(this.tweets);</span>

    <span class="hljs-comment">/* Tranform the object into a string  */</span>
    stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    <span class="hljs-comment">//console.log(stringTweets);</span>

    <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
},
</code></pre>
<p>The code above does four things:</p>
<ul>
<li>takes the tweets array and adds in it an object to represent a single tweet with text and date properties. To the text property, we assign the value of the <code>tweetMsg</code> that is bound with the <code>textarea</code>. For the date, we create a new date object with the <code>new Date().toLocaleTimeString()</code> method.</li>
<li>we empty the value of the <code>tweetMsg</code></li>
<li>we transform the tweets property into a string using the method <code>JSON.stringify(this.tweets)</code> </li>
<li>Then we add it to the local storage.</li>
</ul>
<p>Our final main.js file now looks like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        },
        sendTweet(){
            <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-comment">/* Empty the tweetMsg property */</span>
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
            <span class="hljs-comment">//console.log(this.tweets);</span>

            <span class="hljs-comment">/* Tranform the object into a string  */</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>

            <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },


    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>Now that we've completed this part, we can show a list of tweets and also handle when the page is refreshed, and the local storage has in it our tweets object. We will need to parse it back and add its content to the <code>tweets</code> property to see the list.</p>
<p>Next, we will learn how to show the list of tweets using a v-for directive.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/xFwfrIciFt0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-how-to-show-a-list-of-tweets-html">How to show a list of tweets - HTML</h3>
<p>Inside our root element, add the following code:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we wrap everything in a div with a class <code>card_tweets</code>. Then we use a v-if directive inside a child section to check if there are tweets in the <code>tweets</code> array <code>v-if="tweets.length &gt; 0"</code>. </p>
<p>Inside this section, we can loop over the tweets array using a <code>v-for="(tweet, index) in tweets"</code> directive. After that we use in-template expressions to show the tweet text property <code>{{tweet.text}}</code> and the data <code>{{tweet.date}}</code>.</p>
<p>After the <code>section</code> we can use a <code>v-else</code> directive to show a message in case there are no tweets stored inside the tweets array <code>&lt;div v-else&gt;No tweets to show&lt;/div&gt;</code>. Done. </p>
<p>Now there's one last thing we need to do, and that is to figure out what to do to remove tweets from the list.</p>
<p>But when the user refreshes the page, everything is gone. So we need to work with the <code>localStorage</code> once again to repopulate our tweets array from it before rendering the root element.</p>
<p>Inside the <code>created</code> lifecycle hook we will now write some code to parse the tweets and save them back in the <code>tweets</code> property:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
<span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
    <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))
}<span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
}
</code></pre>
<p>Here we used the <code>localStorage</code> API to first check if there was a key called <code>simple_tweet_tweets</code>. If so, we grab the tweets property using <code>this.tweets</code> and assign to it the content of the <code>localStorage</code>. But we parse the string back to <code>JSON</code> with <code>JSON.parse</code> so we write <code>this.tweets = JSON.parse(localStorage.getItem('simple_tweet_tweets'))</code>.</p>
<p>Now everything works. After we refresh the page, the tweets are still there. Let's move on. In the next step, we will add a method to remove tweets from the list.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/3DzBkUHH3bU">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-how-to-remove-tweets">How to remove tweets</h3>
<p>Inside the div that contains the tweet message, we can add another div to show a link and a trash icon. This lets the user click it and remove that tweet. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we simply used a v-on short syntax directive on the div and invoked a method <code>removeTweet(index)</code>, passing to the method the element index so that we know what to remove.</p>
<p>Let's build our <code>removeTweet</code> method now:</p>
<pre><code class="lang-js">removeTweet(index){
    <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
    <span class="hljs-keyword">if</span>(removeIt) {
        <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
        <span class="hljs-comment">/* Remove the item also from the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    }
}
</code></pre>
<p>This bit of code is pretty straightforward. Our method accepts an index that represents the position of the tweet object in the array obtained from the v-for directive when the method is invoked. </p>
<p>We then create a variable to ask the user to confirm that they want to delete the tweet. We used the <code>confirm</code> function for that. </p>
<p>If the value of the <code>removeIt</code> variable is true, then we execute the code and use <code>this.tweets.splice(index, 1)</code> to remove the tweet from the array using its index. </p>
<p>Finally we update the <code>localStorage</code> value by assigning to is the new array using the <code>localStorage.simple_tweet_tweets = JSON.stringify(this.tweets)</code>.</p>
<h3 id="heading-final-code">Final Code</h3>
<p>Our code is now complete. You can find the final code below or inside the repository here: [https://bitbucket.org/fbhood/simple-tweet-app/src/master/].</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vue 2 Hello World<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-DZqqY3PiOvTP9HkjIWgjO6ouCbq+dxqWoJZ/Q+zPYNHmlnI2dQnbJ5bxAHpAMw+LXRm4D72EIRXzvcHQtE8/VQ=="</span>
        <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                        Send your tweet
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>JavaScript file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: []
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }, 
        sendTweet(){
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;

            <span class="hljs-comment">//console.log(this.tweets);</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },
        removeTweet(index){
            <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
            <span class="hljs-keyword">if</span>(removeIt) {
                <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
                <span class="hljs-comment">/* Remove the item also from the local storage */</span>
                <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            }
        }
    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }

        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }
        <span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
            <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))

        }<span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
        }
    }

});
</code></pre>
<p>We are ready to move forward with our Vue journey. Now it's time to learn about components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/3DzBkUHH3bU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-vue-component-basics">Vue Component Basics</h2>
<p>A component is a reusable block of code that represents a specific element on the page.</p>
<p>Every web page and web or mobile application can be divided into components. Starting from the main sections we can further divide these into smaller bits and make sub-components. </p>
<p>Every component is reusable and is made of dedicated HTML, CSS, and JavaScript code. </p>
<p>We can use components to organize our code and build complex layouts that are easily maintainable.</p>
<p>Looking at a simple web page, it is usually made of a header, the main content area, and a footer. But each of these three pieces can be sub-divided into smaller parts. </p>
<p>For instance, a header can have the main navigation menu, a secondary menu, and a hero image. The same is true for the main and footer areas.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/wrqjPka7puo">YouTube here</a> or at the end of this section to review. You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/12-components-basics/">here</a>.</p>
<p>To get started with components, we first need to learn how to register them, pass them data, and then we need to learn how to use them. Here are some great overviews of these topics to get you started:</p>
<ul>
<li>Register a component (https://vuejs.org/v2/guide/components-registration.html)</li>
<li>How to use Props (https://vuejs.org/v2/guide/components-props.html)</li>
<li>How to use Slots (https://vuejs.org/v2/guide/components-slots.html)</li>
<li>How the Data object works inside a component   </li>
<li>Child Component Events (https://vuejs.org/v2/guide/components-custom-events.html)</li>
<li>Dynamic Components ( https://vuejs.org/v2/guide/components-dynamic-async.html)</li>
</ul>
<h3 id="heading-how-to-register-a-component-in-vue">How to register a component in Vue</h3>
<p>To register a component, we need to use the <code>component</code> method on the <code>Vue()</code> object. After calling this function we need to define a template property with some markup specific to the component. </p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'component-name'</span>, {
    <span class="hljs-comment">// component properies here</span>
});
</code></pre>
<p>Every component needs to have a template property at least – without it a component doesn't make much sense.</p>
<p>So the next step is to define a template property and pass to it a string literal with some HTML tag:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'test-component'</span>, {
    <span class="hljs-comment">// component properies here</span>
    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;p&gt;I am a component&lt;/p&gt;`</span>

});
</code></pre>
<p>Now we can use our component multiple times inside our main HTML file by using its name as it was a standard HTML tag.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">test-component</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">test-component</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>However, our component will always render the same content, <code>I am a component</code>. Let's make it more useful and, following our tweets example, build a tweet message component.</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; Tweet text goes here &lt;/p&gt;
           &lt;p&gt; Date of the tweet goes here&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>OK, now that we have the base for our component we need to actually pass data to it.</p>
<p>One thing to notice here is that every component requires a single root element inside the template property. So, since we have two paragraphs, we wrapped them inside a div that will be considered the root element of our component. </p>
<p>Inside it, we can put whatever we want to build our custom component.</p>
<p>Let's move on to the next step and pass some data to the component.</p>
<h3 id="heading-how-to-use-props-in-vue">How to use props in Vue</h3>
<p>Now, given what we've learned so far, we want to pass data to our component as we did previously to bind data between the Vue instance and the markup file via the moustache syntax. </p>
<p>However, with components things work a bit differently. We use props to create a binding between our component and its template.</p>
<p>The <code>props</code> property can be defined as an array or as an object. 
When used as an array, we can specify the properties as strings inside the array and these can later be used inside the component like we usually do. </p>
<p>When we use an object we can pass the prop as the key and its type as the value. That will help to make sure that the exact data type is passed to our component. </p>
<p>Let's see an example of that.</p>
<p>Example of props as an Array:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'date'</span>]
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Use props as an object where the key is the property and the value is its type:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Once we have defined our properties, we can use them as HTML attributes and pass them the data we want our component to render onto the page. </p>
<p>For instance, we can use the component above to show a bunch of tweets using our newly created component.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- Manually pass the data to the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"25/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"26/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"27/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Pass a javascript expression to the date property of the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">:date</span>=<span class="hljs-string">"new Date().toLocaleString()"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
</code></pre>
<p>The first examples will render the string we passed between quotes. But 
to render the computed result of the new <code>Date()</code> instance we will need to use the v-bind directive so that its content is interpreted as JavaScript code.</p>
<p>You can review all this in the docs here: [https://vuejs.org/v2/guide/components-props.html].</p>
<h3 id="heading-the-data-property-inside-components">The data property inside components</h3>
<p>So far we have seen that we can bind data by defining properties inside the <code>data</code> object on a Vue instance. </p>
<p>When working with components the data object is not available as an object but as a function. This function can return an object with properties. This will make each component's instance unique and independent from the others.</p>
<p>Following our previous example, let's add a couple of CSS classes to our component.</p>
<p>First, we will edit our component template and bind the class attribute to a data property. Then we will create our data object.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Now our template will look for two data properties, <code>tweetBoxWrapper</code> and <code>dateClass</code>, that we can later use inside our CSS to add some style to our elements. Let's add the data function now.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-comment">// Data properties go here</span>
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
        }
    }
});
</code></pre>
<p>Another thing we can do is to define a data property and use it inside our template, for instance, to dynamically show the current date. We can define a <code>now</code> property and use it in the template like we previously did with the <code>date</code> property:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), <span class="hljs-comment">// 3 </span>

        }
    }


});
</code></pre>
<p>In the example above we have used both <code>props</code> and <code>data</code>. We can use the prop <code>text</code> as an attribute when we use our component <code>&lt;tweet-message text="This is a component"&gt;&lt;/tweet-message&gt;</code>. The properties we returned in the <code>data</code> method are bound to the template and will render the information we specify right there in the data method.</p>
<p>When inside the data method, we need to remember that props defined here are accessible using the <code>this</code> keyword.</p>
<p>So if we want to store the value of the text prop inside a property in the data object, we can grab it like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), 
            <span class="hljs-attr">message</span>: <span class="hljs-built_in">this</span>.text
        }
    }


});
</code></pre>
<p>Next, we will learn about slots.</p>
<h3 id="heading-how-to-use-slots">How to use slots</h3>
<p>There are situations when we just don't know or want to strictly define what goes inside a component. Or we might want to let the user decide its content when they use our component.</p>
<p>In such cases, we can use slots when we declare the template of our component.</p>
<p>Let's imagine that we have another component that we want to use to divide tweets into different sections.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-section'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'title'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
        &lt;div class="tweet_section"&gt;
            &lt;h2&gt;{{title}}&lt;/h2&gt;
           &lt;slot&gt;&lt;/slot&gt;
       &lt;/div&gt;  
    `</span>    
});
</code></pre>
<p>Our new component can be as simple as that, a div with a class <code>tweet_section</code>, an <code>h2</code> that binds to a prop, and a slot. The slot means that inside our component we can put whatever we want, like nesting other elements and even other components.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Latest Tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my first tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my second tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my third tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my fourth tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Most popular"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Trendy in IT<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a very popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is another popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
</code></pre>
<p>We've barely scratched the surface here, but with what we know we can already modify our <code>simple_twitter</code> application to use components. Along the way, we will also learn how events work inside components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/wrqjPka7puo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-update-your-simpletwitter-project-with-components">How to Update Your Simple_twitter Project with Components</h2>
<p>Now that we have a basic understanding of components, we can update the simple Twitter project we built in the previous videos and use components to make our code better.</p>
<p>We need to do a few things to make this happen, and create a component:</p>
<ol>
<li>We need to decide what component we want to build </li>
<li>We need to extract the code from the markup and place it in the template property</li>
<li>We need to refactor our code to make the component work.</li>
</ol>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/HanHyGFC6Sc">YouTube here</a>
or checkout the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/13-simple-twitter-components/">here</a>.</p>
<p>Let's say we want to create a component for the tweet message. </p>
<h3 id="heading-how-to-create-the-component">How to create the component</h3>
<p>Let's create a component for a tweet message like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">``</span>
});
</code></pre>
<h3 id="heading-how-to-move-the-tweetmsg-element">How to move the tweetMsg element</h3>
<p>Then we have to move the <code>tweetMsg</code> element inside the  <code>template</code> property of our component:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg" v-for="(tweet, index) in tweets"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>After that, we need to update the template because the v-for directive now is useless. So we will remove it and add it back later when we are ready to use the component.</p>
<p>Given that we will not have a v-for directive at this point, we still want to use the tweet variable to grab the tweet, so we will pass it as a <code>props</code>.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<h3 id="heading-how-to-emit-a-custom-event">How to emit a custom event</h3>
<p>There is also an event listener that needs to change to let our application work as expected. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The code here <code>&lt;div class="tweet_remove" @click="removeTweet(index)"&gt;</code> listens to click events so the user can remove a tweet by clicking on it. </p>
<p>This will need to go, and we need to replace it with a special method of the Vue instance called <code>$emit()</code>. Our component instance will need to communicate with the parent instance and tell it that it wants to trigger the remove tweet method.  </p>
<p>To solve this problem, Vue provides a custom events system. It allows us to use the v-on directive to listen not only to native DOM events but also to custom events defined at the component level.</p>
<p>We need to update this line of code: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
</code></pre>
<p>and change it like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"$emit('remove-tweet', 'index')"</span>&gt;</span>
</code></pre>
<p>Let's break this down: we keep using the v-on directive in its short form <code>@</code>. Then we use the Vue <code>$emit</code> method to define a custom event that our component will emit when we click on this element. </p>
<p>To the <code>$emit</code> method we pass two parameters, the first is the name of the custom event <code>remove-tweet</code>, and the second is a parameter that we want to pass to the event listener when we use <code>index</code>. That will be the index of the element we want to delete. </p>
<p>So that the parent instance can listen to our event, trigger the <code>removeTweet</code> method we defined in the main Vue instance and remove the correct tweet.</p>
<h3 id="heading-put-it-all-together-1">Put it all together</h3>
<p>Our final component now looks like this:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{tweet.text}}
        &lt;/p&gt;

        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar-alt fa-sm fa-fw"&gt;&lt;/i&gt;{{tweet.date}}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="$emit('remove-tweet', 'index')"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-xs fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;

    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>And we'll change our index.html file as follows:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>  <span class="hljs-attr">v-bind:tweet</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"index"</span> @<span class="hljs-attr">remove-tweet</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that we've completed our first project, let's learn how to make an API request and how to use the GitHub API to build our final portfolio.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/HanHyGFC6Sc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-perform-api-calls-with-axios">How to Perform API Calls with Axios</h2>
<p>For our next project, I have created a simple but nice design using Figma that we will use to kick start our portfolio.</p>
<p>Our portfolio will use the GitHub's rest API to pull projects and fill out the design. </p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/XJEmPr89HA8">YouTube here</a>
and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/14-axios/">BitBucket</a>.</p>
<h3 id="heading-what-is-a-rest-api">What is a REST API?</h3>
<p>To quote Wikipedia:</p>
<blockquote>
<p>"A REST API is a software architectural style that enables the requesting system to access and manipulate a textual representation of web resources."</p>
</blockquote>
<p>What this means is that our Vue application (the requesting system) will request a textual representation from GitHub of our repositories that we can use later and manipulate to showcase our projects inside our portfolio.</p>
<p>For our final project, we will use a library called Axios that will help us make HTTP requests to the GitHub API. </p>
<p>We can install Axios inside our project in multiple ways. For our example we will keep things simple and use the CDN. </p>
<p>There are also other methods you can use to install Axios. The official documentation for Axios is available <a target="_blank" href="https://www.npmjs.com/package/axios">here</a>
and you can read about how to consume the API in the <a target="_blank" href="https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Base-Example">documentation here</a>.</p>
<h3 id="heading-how-to-install-axios-via-cdn">How to install Axios via CDN</h3>
<p>So let's get started and install Axios via the CDN. We will use the UNPKG CDN and insert a script tag inside our main HTML file. </p>
<p>This CDN will always provide the most up to date version of Axios. Alternatively, we can also specify a different version number. </p>
<p>Let's start by inserting the following script in an index.html file that we will use to send our first HTTP request to the GitHub API.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Our final HTML file will look like this now:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS / GitHub API<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>


<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The code above is nothing new, but let's look at it piece by piece.</p>
<p>We have a basic HTML structure. Before the closing body tag, we've placed two script tags, one for Axios and one for VueJs. </p>
<p>In the body, we created a root element for the Vue application that we called <code>#app</code>.</p>
<p>Finally, before the end of the body tag, we placed a new script tag that points to the file where we will write our code, the main.js file.</p>
<p>Now we have all the building blocks to make our first API call and request data from the GitHub API.</p>
<p>But before that let's quickly see what an HTTP request actually is and what kind of requests we can make.</p>
<h3 id="heading-what-is-an-http-request">What is an HTTP request?</h3>
<p>HTTP stands for Hypertext transfer protocol. It is an application-layer protocol designed for communications between two points:</p>
<ol>
<li>a web client (the browser) </li>
<li>a web server</li>
</ol>
<p>This protocol allows transmission of data like HTML files and. It defines verbs also known as methods that you can use to perform specific actions on a given resource.</p>
<p>The method or verb that we will use for our project is the <code>GET</code> method, that, as you might have guessed, is used to obtain or to get 
a resource from the webserver.</p>
<p>We have also other methods:</p>
<ul>
<li>GET (retrieves data)</li>
<li>POST (sends data)</li>
<li>PUT (updates the entire representation of the data)</li>
<li>PATCH (similar to put but used to partially update data)</li>
<li>DELETE (removes data)</li>
</ul>
<p>Each of these requests performs a specific action on a resource, but there are also other verbs like the HEAD, OPTIONS, CONNECT, and TRACE.</p>
<p>I won't cover HTTP in detail here as it's out of the scope of this guide. But below there are some links to documentation pages 
related to this topic if you want to find out more. </p>
<p>I suggest you to read the following at least:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-the-internet-works/">HTTP Intro</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP Methods</a></li>
</ul>
<h3 id="heading-how-to-perform-a-get-request">How to perform a GET request</h3>
<p>We will use the GET method to perform get requests from the GitHub API. All data we want to request are publicly accessible (the public repositories of a user), therefore we don't have to authenticate our application. </p>
<p>But unauthenticated requests are limited. For the scope of this tutorial, this is perfectly fine. If you plan to put this in production then you might want to look at how to make authenticated requests and obtain an API key from GitHub.</p>
<p>GitHub provides clear and in-depth documentation about its Rest API, including a list of resources that you can request along with their endpoints. We will use the "List repositories for a user" resources available <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-repositories-for-a-user">here</a>.</p>
<p>Let's look at the documentation. The first thing we notice is that GitHub gives us a GET endpoint where we can send our HTTP requests <code>/users/{username}/repos</code>. </p>
<p>The placeholder <code>{username}</code> needs to be replaced with the actual username of the user we want to request the list of public repositories from.</p>
<p>From the documentation, we also see that there are other parameters that we can use to refine our request. We will use <code>username</code> that goes in the path and needs to be a string as described in the parameters table under Type. </p>
<p>We can also use the <code>per_page</code> and <code>page</code> parameters to paginate our results.</p>
<p>Let's make the first request and see what we get.</p>
<p>Inside our main.js file, we will create a new Vue instance and add a <code>mounted</code> lifecycle hook where we will perform the HTTP request using Axios.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>:{
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
    },
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {<span class="hljs-built_in">console</span>.log(error);})
    }
});
</code></pre>
<p>Let's break this code down. First, we have created a new Vue instance. Then we used the <code>el</code> property and assigned it a root HTML element. </p>
<p>Then we have defined a <code>data</code> object and the properties that we will use later to perform the HTTP request and handle the response.</p>
<p>After the data object, we have defined a lifecycle hook that will use to run our code once the root element has been mounted.</p>
<p>Inside the <code>mounted</code> method, it's time to use Axios and perform an HTTP request. </p>
<p>Axios is a promise-based HTTP client. When we use the get method to request our data from the GitHub API it will return a promise that needs to be handled. </p>
<p>We do this using the syntax <code>axios.get()</code> to perform the request, then we handle its response using the <code>.then()</code> method on the promise. </p>
<p>If our request fails the <code>.catch()</code> method will handle the error and, in this case, show the error message on the console.</p>
<p>Promises are out of the scope of this guide, but if you want to learn more, you can check out <a target="_blank" href="https://www.freecodecamp.org/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/">this detailed article here</a>.</p>
<p>Inside the <code>.get()</code> method we have put the URL including a query string that uses <code>per_page</code> and <code>page</code> parameters to submit our request. Inside the <code>.then()</code> method we handled the response. The response parameter is given to us by the promise and we use an arrow function to handle it.</p>
<pre><code class="lang-js">response =&gt; {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
</code></pre>
<p>The get method returns a promise. Here we simply handled its <code>response</code> with an arrow function where <code>response</code> is the return value that we obtained by calling <code>axios.get()</code>.</p>
<p>We logged the response object to the console. Then we assigned its content, the <code>response.data</code>, to our <code>projects</code> property so that we can later retrieve each project and show them onto the page as usual with a <code>v-for</code> directive.</p>
<h3 id="heading-how-to-show-each-project">How to show each project</h3>
<p>Now it's time to show our projects inside the portfolio. We can do that with the v-for directive.</p>
<p>The projects property in this case contains an array of objects. Each object has its properties that we can use to populate our template.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here we use the v-for directive to loop over the array of projects.
Now the <code>project</code> variable contains an object that represents a single repository from the GitHub account.</p>
<p>Looking at the response object we know that we can grab a number of properties. So we picked <code>full_name</code>, the full name for the repository, <code>owner.avatar_url</code>, the URL of the profile's avatar, and <code>html_url</code> that is the actual URL of our repository. That's all we need for now. </p>
<p>If we now look at the page we will immediately see all repositories from our account.</p>
<p>Now that we know how to make an HTTP request with Axios and get data from GitHub, we are almost ready to start building our portfolio. </p>
<p>In the next section, we are going to look at another Vue library called Vue-router that we will use in our final project.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/XJEmPr89HA8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-handle-routing-with-vuerouter">How to Handle Routing with VueRouter</h2>
<p>Our portfolio will surely have more than one page, so we need a system that understands where to send the user when, for instance, they click a link in the navbar for a specific page. </p>
<p>For that Vue has an official routing package that can help us do just that and build a single page application.</p>
<p>A single page application is an application that doesn't refresh the page when a user visits a new page so that the user experience is more fluid.</p>
<p>As for Vue and Axios, we need to install this library and we do that via its CDN. But as always, there are also other methods depending on your needs. I just want to keep things simple for now, so let's start by placing the CDN script tag inside the HTML file and learn the basics of this new library.</p>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/T_avTRFAEAg">YouTube here</a>
and checkout the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/15-routing/">BitBucket</a>.</p>
<p>You can also see the Vue Router Documentation <a target="_blank" href="https://router.vuejs.org/installation.html#direct-download-cdn">here</a>.</p>
<h3 id="heading-how-to-install-vue-router-via-cdn">How to install Vue Router via CDN</h3>
<p>Let's take our previous example index.html and after the VueJS CDN will point to the router <code>https://unpkg.com/vue-router@3.4.9/dist/vue-router.js</code>.</p>
<pre><code class="lang-html">
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS / GitHub API<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> 
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"view"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Main scrip file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-use-vue-router">How to use Vue Router</h3>
<p>Now our app has access to the router system and we can add a couple of routes for our application.</p>
<p>We can do so using the <code>router-link</code> component provided by the library and its <code>to</code> attribute to point the link to a specific page.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a router link using the 'router-link' component and set the path using the 'to' attribute --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
</code></pre>
<p>We also used the router-view component that will render a specific component for each route.</p>
<p>Now we need to do something inside our JavaScript file to make this work. </p>
<p>Let's see the steps we need to take:</p>
<ul>
<li>Define route components</li>
<li>Define routes</li>
<li>Create a Vue router instance</li>
<li>Create and mount the Vue root instance.</li>
</ul>
<p>First, we need to define our components that we'll use from each route to render the content of the page.</p>
<p>We will create two components, one for the home page and one for the projects page.</p>
<p>To simplify the steps, we will keep everything in the same file and refactor later on. </p>
<p>Let's create the first two basic components to see if the router works:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create Route components</span>
<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt; Projects &lt;/div&gt;'</span>}
</code></pre>
<p>Now let's follow the remaining steps and define the routes, create the vue router instance, and create and mount the Vue root instance.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>That's it. If you visit the homepage you will see two navigation links and the site content will change accordingly.</p>
<p>Let's put it all together and start building our portfolio.</p>
<p>From the previous example in the Axios section, we requested from the GitHub API all public repositories for a user and rendered name, user avatar, and project URL onto the page. </p>
<p>Let's move some of that logic inside our application that uses routes.</p>
<p>The main changes that we need to make here are:</p>
<ul>
<li>move the HTML markup inside the <code>template</code> property of the project's component</li>
<li>move the <code>data</code> properties inside the <code>data</code> object of the component</li>
<li>move the code we wrote in the mounted hook inside our component.</li>
</ul>
<p>The final code looks something like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Define route components</span>

<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt; 
         &lt;div v-for="project in projects"&gt;
            &lt;h2 class="title"&gt;{{project.full_name}}&lt;/h2&gt;

            &lt;div class="author"&gt;
                &lt;img width="50px" :src="project.owner.avatar_url" alt="me"&gt;
            &lt;/div&gt;
            &lt;div class="view"&gt;
                &lt;a :href="project.html_url"&gt;View&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;`</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">projects</span>: [],
            <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
            <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
        }
    }, 
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-comment">//console.log(response);</span>
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {<span class="hljs-built_in">console</span>.log(error);})
    }
} 

<span class="hljs-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>The HTML file remains the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that we have a base to work with, let's improve it. We will use a design prototype I made using Figma and add some functionalities to our portfolio to make it look nice.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/T_avTRFAEAg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify">Final Project – How to Build a Portfolio with VueJS, VueRouter, Axios, GitHub API and deploy to Netlify</h2>
<p>We are ready to build our final project! For our Vue-folio, we will start from where we left off in the previous section.</p>
<p>We will build a single-page application that has two routes, one for the home page and one for the projects page. </p>
<p>Below are the building blocks:</p>
<ul>
<li>Vuejs </li>
<li>Vue router</li>
<li>Axios</li>
<li>GitHub rest API</li>
<li>portfolio design</li>
</ul>
<p>You can watch this tutorial on <a target="_blank" href="https://youtu.be/I6hQnWQU4rQ">YouTube here</a> and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/16-final-project-portfolio/">BitBucket here</a>.</p>
<h3 id="heading-project-structure">Project structure</h3>
<p>To speed things up we will just copy the code we wrote in the previous section.</p>
<p>The project structure will be the following:</p>
<pre><code>|-- index.html
|-- assets/
    |-- css/
        |-- style.css
    |-- js/
        |-- main.js
    |-- img/
</code></pre><h3 id="heading-indexhtml-file">index.html file</h3>
<p>The index.html file is a little different from what we had in the previous section. Here, we will place only the router-view component that is responsible for showing the component matching a given route. </p>
<p>Then we will place the actual <code>route-links</code> inside each component to make sure we have the desired result as per the design.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vuefolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./assets/css/style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-stylecss-file">style.css file</h3>
<p>Since this is not going to be a CSS tutorial, for the CSS part you can simply copy the code from the repository file if you are following along.</p>
<pre><code class="lang-css">
    <span class="hljs-comment">/* Utility Classes */</span>
    <span class="hljs-selector-class">.d_none</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.d_flex</span> {
        <span class="hljs-attribute">display</span>: flex;
    }
    <span class="hljs-selector-class">.container</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
        <span class="hljs-attribute">margin</span>: auto;
    }
    <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: white;
    } 
    <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.loading</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-comment">/* END Utility Classes */</span>
    <span class="hljs-comment">/* Components */</span>
    <span class="hljs-selector-class">.bio__media</span> {
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: flex-start;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">text-align</span>: left;
    }
    <span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

    }

    <span class="hljs-selector-class">.card__custom</span> {
        <span class="hljs-attribute">position</span>: relative;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-between;
    }
    <span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
        <span class="hljs-attribute">text-align</span>: right;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">justify-content</span>: space-around;
        <span class="hljs-attribute">overflow</span>: hidden;

    }
    <span class="hljs-selector-class">.card__custom__img</span> {

        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
        <span class="hljs-attribute">background-position</span>: center;
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">display</span>: inline-block;
        <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">box-sizing</span>: border-box;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">cursor</span>: pointer;
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
    }
    <span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-comment">/* END Componenet */</span>
    * {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">box-sizing</span>: border-box;
    }

    <span class="hljs-selector-tag">body</span>{
        <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

    }
    <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-decoration</span>: none;   
    }

    <span class="hljs-comment">/* Home Page */</span>
    <span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: center;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
        <span class="hljs-attribute">font-style</span>: normal;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
        <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> {
        <span class="hljs-attribute">position</span>: relative;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">margin</span>: auto;
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

    }

    <span class="hljs-comment">/* Header */</span>
    <span class="hljs-selector-id">#site_header</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">justify-content</span>: space-between;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-comment">/* Portfolio Page Section */</span>

    <span class="hljs-selector-id">#portfolio</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-around;
    }
    <span class="hljs-selector-class">.btn_load_more</span> {

    }
    <span class="hljs-comment">/* Skills */</span>

    <span class="hljs-selector-id">#skills_section</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">background-position</span>: top left;
    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
        <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
        <span class="hljs-attribute">list-style</span>: none;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
    }



    <span class="hljs-selector-class">.avatar</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: auto;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

    }




    <span class="hljs-selector-class">.card__back</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.rotate__card</span> {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
    }
    <span class="hljs-comment">/* Site Footer */</span>

    <span class="hljs-selector-tag">footer</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    }



    <span class="hljs-comment">/* Media Query  */</span>

    <span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
        <span class="hljs-selector-class">.card</span> {
            <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }
    }
</code></pre>
<h2 id="heading-mainjs-file-basic-structure">Main.js file basic structure</h2>
<p>Inside the main.js file, we have the core of our single page application. 
Here we will define the route components that need to be rendered for each view/page, the homepage, and projects components. </p>
<p>Then we will define two routes, one for the homepage and one for the projects page, create a router instance, and pass it to the routes. Finally, we will create a new Vue instance and pass to it the router instance and mount the root HTML element.</p>
<p>Let's start with the route components.</p>
<h3 id="heading-how-to-define-components-for-each-view">How to define components for each view</h3>
<p>The homepage component is fairly simple. </p>
<pre><code class="lang-js"><span class="hljs-comment">// Homepage component</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;
                &lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
</code></pre>
<p>Let's break it down. First, we create a Home constant that will hold the router component object. </p>
<p>Inside the object, the only thing we will put is the template property with some markup to render our page. The main thing to notice here is the router-link component that will point to the /projects route from the homepage.</p>
<p>Next, let's create a route component for the projects page. We have a lot to do here so for now let's just add some boilerplate code – we will come back to it later and write out step by step all the logic. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;h1&gt;Projects&lt;/h1&gt;
    &lt;/div&gt;`</span>,
    data() { 
        <span class="hljs-keyword">return</span> {
                <span class="hljs-comment">// Data object here</span>
            }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// All methods here</span>
    },
    mounted(){  
        <span class="hljs-comment">// Lifecycle hook      </span>

    }
}
</code></pre>
<p>The Projects route components have a <code>template</code> property that so far holds a basic markup that only spit out an <code>h1</code> title.</p>
<p>After that there is the component's data method that returns an empty object, then an empty methods object and an empty lifecycle hook.</p>
<p>That's all we need, for now, so let's move on and define the rest of the building blocks, the routes, the router and the Vue instances.</p>
<h3 id="heading-how-to-define-the-routes-router-and-vue-instance">How to define the routes, router, and Vue instance</h3>
<p>Now that we have two components to render on our main pages we can move forward to the next steps:</p>
<ul>
<li>define routes</li>
<li>create the router instance</li>
<li>create and mount the Vue instance</li>
</ul>
<p>First, let's define our two routes and link the components.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];
</code></pre>
<p>In the code, we have defined a new constant called routes. In it, we defined two routes as an array of objects. </p>
<p>Each object has two properties:</p>
<ul>
<li>path</li>
<li>component</li>
</ul>
<p>The first object is for the homepage. Its path will respond to requests made to our website base URL, like https://fabiopacifici.com/.</p>
<p>Then the component property links this page to the route's component called <code>Home</code> that we defined in the previous step. </p>
<p>The second object is for the projects page. The path responds to requests made to <code>/projects</code> and it's linked to the <code>Projects</code> route component.</p>
<p>Now that we have our routes:</p>
<pre><code class="lang-js">
<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})
</code></pre>
<p>Above we used the ES6 syntax that allows us to just put the name of the variable holding the routes since it is equal to the name of the property that we needed to use. It's actually the same as writing <code>routes: routes</code>.</p>
<p>Now, we create a Vue instance. Inject the router instance inside it and finally mount the root element. </p>
<pre><code class="lang-js">
<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>Done! We now have everything in place to start building our portfolio and complete the Projects route component.</p>
<h2 id="heading-how-to-build-the-main-projects-route-component">How to Build the Main Projects Route Component</h2>
<p>We will start working on the data object. Here we need to define properties that will hold all our projects once we fetch data from the git hub API.</p>
<h3 id="heading-the-data-object">The data object</h3>
<p>To keep things easier I have intentionally limited results to 20. If you feel this isn't enough you can change the code as you like. </p>
<p>You can implement pagination for your results by increasing the page property that will be passed to the query string or return more results per page by increasing the value of the <code>perPage</code> property.</p>
<pre><code class="lang-js">data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
</code></pre>
<p>As we learned in the section where we used Axios to fetch data from the GitHub REST API, there are a few properties we need to define. </p>
<p>The component's data function returns an object with a <code>projects</code> property where we will store all projects we fetch from GitHub.</p>
<p>Then we add a <code>projectsList</code> property that holds only a few projects at a time. We will use this property later to implement a very simple
load more feature in combination with the <code>projectsCount</code> property.</p>
<p>Then we have a <code>skills</code> property where we will store all languages used to build our projects. </p>
<p>We'll use the <code>perPage: 20</code> and <code>page: 1</code> properties to build the query string used to fetch data from GitHub. It will take 20 projects and 
return only the first page of results unless we change these values.</p>
<p>Finally, we have a <code>loading: true</code> property that we will use to check if the page is fetching data and an <code>errors: false</code> property that shows an error message in case we are unable to connect to the GitHub server.</p>
<p>In the next step, we will start working on all methods required to make our application work.</p>
<h3 id="heading-the-fetch-all-data-method">The fetch all data method</h3>
<p>The first method is the one we will use to fetch data from GitHub.</p>
<p>This method will make the Ajax call to the GitHub rest API using Axios and store the response in a property of the Vue instance:</p>
<pre><code class="lang-js">
 <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {

                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {
                <span class="hljs-built_in">console</span>.log(error);
                <span class="hljs-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        },
</code></pre>
<p>Let's break this down. First, we defined a method called <code>fetchData: function(){}</code>. This method uses Axios to make an API call to the
REST API. </p>
<p>In the <code>.get()</code> method we have built the URL also using the properties <code>perPage</code> and <code>page</code> as part of the query string.</p>
<p>The get method returns a promise so we used the <code>.then()</code> method on the promise to handle the response using an arrow function <code>response =&gt; {}</code>.</p>
<p>Inside the arrow function, we stored the response data inside the projects property of the Vue instance using <code>this.projects = response.data;</code>.</p>
<p>Next, we used a <code>forEach</code> loop to iterate over each project and store the language used in the repository as a skill using the code below:</p>
<pre><code class="lang-js"><span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
    <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
        <span class="hljs-built_in">this</span>.skills.push(project.language)
    };
});
</code></pre>
<p>We chained a <code>.catch</code> method to handle an error in case we are unable to connect to the rest API and fetch data. We will log the error to the
console and update the value of the <code>errors</code> property to true so that we can show a custom error message to the user later on. </p>
<p>Finally, we chained the <code>.finally()</code> method that will be executed after the response has been handled. We also updated the <code>loading</code> property and set it to false so that we can show the results to the user. </p>
<p>Inside the <code>finally</code> method we can also call a method (that we still have to create) and that we will use to slice the results later. </p>
<p>Let's build it.</p>
<h3 id="heading-the-get-projects-method">The get projects method</h3>
<p>This method takes a portion of the projects we actually stored in the <code>projects</code> property. We can use the <code>projectsList</code> property to store the slice and later implement a method to increment them with a show more button.</p>
<pre><code class="lang-js">
<span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

    <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

},
</code></pre>
<p>The getProjects method takes a portion of all projects stored in the <code>projects</code> property using the array slice method in conjunction with
the property <code>projectsCount</code> that is set to five. So it will store in there only the first five results and return them.</p>
<p>To add five more projects to the <code>projectsList</code> property we will also need a method that the user can call when he clicks on the load more button. Let's create it.</p>
<h3 id="heading-the-load-more-projects-method">The load more projects method</h3>
<p>The load more method will first check if the length of the <code>projects</code> array is less than or equal to the length of the <code>projectsList</code> array. Then, if not, it will increment the value of the <code>projectsCount</code> property by five and then take a bigger slice from the <code>projects</code> property.</p>
<pre><code class="lang-js">
loadMore(){

    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
        <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
        <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
    }


}
</code></pre>
<h3 id="heading-build-the-template">Build the template</h3>
<p>In the template property of the <code>Projects</code> component, we can start with the header section. We'll also put in there two <code>router-link</code> components for the pages navigation:</p>
<pre><code class="lang-html">`<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_flex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Next, we can continue working on the template and create the main section.
We will put the following markup always in the main template div, right under the header closing tag.</p>
<p>Let's start by placing the main container:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show an error message if the REST API doensn't work --&gt;</span>
    <span class="hljs-comment">&lt;!-- Otherwise show  a section for our portfolio projects and skills section--&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
</code></pre>
<p>Inside the container, let's use the v-if-else directives to show an error message or the projects section:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show Errors if the rest api doesn't work --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
        Sorry! It seems we can't fetch data righ now 😥
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Else show the portfolio section --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>To make the code work, we used the v-if directive and passed to it the <code>errors</code> property. This property will be set to <code>true</code> if there is an error while we fetch data from GitHub or will be set to <code>false</code> if everything is ok. So the v-else directive will render the portfolio section.</p>
<p>Next, we need to show a 'loading...' message while we fetch data. When done we can use the v-for directive to loop over the results. So right in the portfolio section, we will write another v-if-else directive.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
 <span class="hljs-comment">&lt;!-- Use a v-if directive to show the loading message --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Here we use the v-if directive <code>&lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;</code> to render a loading message. After that the <code>&lt;div v-for="project in projectsList" class="card__custom" v-else&gt;&lt;/div&gt;</code>
has two directives, the v-for directive that we use to loop over the <code>projectsList</code> property and a v-else directive that will show this element when we are done fetching data from GitHub.</p>
<p>Now we can use the <code>project</code> variable to render all project details in our markup:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__text"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>                        
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_flex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"date"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__img"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_custom__button"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
            Code
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>To render the project title and desciption we used the propeties <code>poject.name</code> and <code>project.description</code>. But the description and the title will break our design unless we trim them at some point. </p>
<p>Next in the element with class <code>date</code> we rendered the poject data in a readable fomat using the <code>new Data().toDateString()</code> method. </p>
<p>To render the user avatar <code>&lt;img class="avatar" :src="project.owner.avatar_url"&gt;</code> we used the shortcut for the v-bind diective so that we could use the property <code>project.owner.avatar_url</code> to grab the avatar URL. </p>
<p>Finally, to render a button that once clicked redirects the user to the repository page we bound the <code>href</code> attribute to the <code>project.html_url</code> property <code>&lt;a :href="project.html_url" target="_blank"&gt;Code&lt;/a&gt;</code>.</p>
<p>Our project card is complete. The next thing we need to do is render a load more button to show more projects. </p>
<p>We are still working inside the <code>projects</code> section. Right after the project card we can write the following markup</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Render a load more button --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">""</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>The v-if directive first checks if the loading property is set to false. 
If so, we'll use another v-if directive to check if the length of property <code>projectsList</code> is less than the length of the property <code>projects</code>. </p>
<p>If so, it will show a button that uses a v-on directive to listen for clicks 
<code>&lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;</code> and trigger a <code>loadMore()</code> method. Otherwise, we show a link to the GitHub account. </p>
<p>After this, we can show a list of skills related to all the projects:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show a skills section --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Loop over the skills property --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our markup is complete, but we need to improve our code a little as the project title and its description are breaking our design! </p>
<p>Let's create two methods, one to trim the title and one for the description text.</p>
<h3 id="heading-the-trimtext-and-trimtitle-methods">The trimText and trimTitle methods</h3>
<p>The <code>trimTitle</code> method will replace all <code>-</code> and <code>_</code> with a space, and restrict the number of characters to 12. The <code>trimText</code> method instead only reduces the number of characters of the description in excess of 100 characters. </p>
<pre><code class="lang-js">trimTitle: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
    <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> title;

},
<span class="hljs-attr">trimText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
    <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
        <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> text;
},
</code></pre>
<p>With these two methods, now we can update the markup and use them to make sure nothing breaks the design.</p>
<p>Let's update these two lines that will be changed from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>To this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Let's put eveything together. The final markup will be the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_flex"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
            Sorry! It seems we can't fetch data righ now 😥
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-else</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__text"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>                        
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_flex"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"date"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span>&gt;</span>

                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom__img"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_custom__button"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                            Code
                        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>


                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>


                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">""</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>  
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>There is one last thing to do. Since fetching data from GitHub is very fast we don't really see the loading message. Let's set a timeout and delay it by a few seconds – then you can tune it as you like.</p>
<h3 id="heading-the-mounted-lifecycle-hook">The mounted lifecycle hook</h3>
<pre><code> mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
</code></pre><p>Inside the mounted lifecycle hook we used <code>setTimeout()</code> and called the <code>fetchData</code> method as the first parameter. Then for the second parameter we specified that this method should be executed after 3000 milliseconds (3seconds). </p>
<h2 id="heading-lets-see-our-final-code-all-toghether">Let's see our final code all toghether</h2>
<p>Index.html file looks like the following:</p>
<pre><code class="lang-html">
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vuefolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./assets/css/style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>And this is the main.js file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create route components</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;&lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
<span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;header id="site_header" class="container d_flex"&gt;
            &lt;div class="bio__media"&gt;
                &lt;img src="./assets/img/avatar.svg" alt=""&gt;
                &lt;div class="bio__media__text"&gt;
                    &lt;h1&gt;John Doe&lt;/h1&gt;
                    &lt;h3&gt;Python Expert&lt;/h3&gt;
                    &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;nav&gt;
                &lt;router-link to='/'&gt;Home&lt;/router-link&gt;
                &lt;router-link to="/projects"&gt;Project&lt;/router-link&gt;
                &lt;a href="https://"&gt;
                    &lt;i class="fab fa-github fa-lg fa-fw"&gt;&lt;/i&gt;
                &lt;/a&gt;
            &lt;/nav&gt;
        &lt;/header&gt;

         &lt;main class="container"&gt;
            &lt;div class="error" v-if="errors"&gt; 
                Sorry! It seems we can't fetch data righ now 😥
            &lt;/div&gt;

            &lt;section id="portfolio" v-else&gt;
                &lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;
                &lt;div class="projects" v-else&gt;
                     &lt;div v-for="project in projectsList" class="card__custom" &gt;
                        &lt;div class="card__custom__text"&gt;
                            &lt;div&gt;
                                &lt;h3&gt;{{trimedTitle(project.name)}}&lt;/h3&gt;
                                &lt;p&gt;{{trimedText(project.description)}}&lt;/p&gt;                        
                            &lt;/div&gt;

                            &lt;div class="meta__data d_flex"&gt;
                                &lt;div class="date"&gt;
                                    &lt;h5&gt;Updated at&lt;/h5&gt;
                                    &lt;div&gt;{{new Date(project.updated_at).toDateString()}}&lt;/div&gt;
                                &lt;/div&gt;
                                &lt;img class="avatar" :src="project.owner.avatar_url"&gt;

                            &lt;/div&gt;
                        &lt;/div&gt;
                        &lt;div class="card__custom__img"&gt;&lt;/div&gt;
                        &lt;div class="card_custom__button"&gt;
                            &lt;a :href="project.html_url" target="_blank"&gt;
                                Code
                            &lt;/a&gt;
                        &lt;/div&gt;


                    &lt;/div&gt;


                    &lt;div style="text-align: center; width:100%" v-if="!loading" &gt;
                        &lt;div v-if="projectsList.length &lt; projects.length"&gt;
                            &lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;
                        &lt;/div&gt;
                        &lt;div v-else&gt;
                            &lt;a href="" target="_blank" rel="noopener noreferrer"&gt;Visit My GitHub&lt;/a&gt;
                        &lt;/div&gt;

                    &lt;/div&gt;

                    &lt;div id="skills_section"&gt;
                        &lt;h2&gt;Development Skills&lt;/h2&gt;
                        &lt;ul class="skills"&gt;
                            &lt;li v-for="skill in skills"&gt;{{skill}}&lt;/li&gt;
                        &lt;/ul&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/section&gt;


        &lt;/main&gt;
    &lt;/div&gt;`</span>,
data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">data</span>: [],
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-attr">trimedTitle</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
            <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
                <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
            } <span class="hljs-keyword">return</span> title;

        },
        <span class="hljs-attr">trimedText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
            <span class="hljs-keyword">if</span>(text === <span class="hljs-literal">null</span>) {
                <span class="hljs-keyword">return</span> <span class="hljs-string">'This project has no description yet!'</span>;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
                <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
            } 
            <span class="hljs-keyword">return</span> text;

        },
        <span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

            <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

        },
        <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {
                <span class="hljs-built_in">console</span>.log(error);
                <span class="hljs-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        }, 
        loadMore(){

            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
                <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
                <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
            }


        }

    },
    mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
}

<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];


<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})

<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>On the CSS side this is what we have:</p>
<pre><code class="lang-css">
<span class="hljs-comment">/* Utility Classes */</span>
<span class="hljs-selector-class">.d_none</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.d_flex</span> {
    <span class="hljs-attribute">display</span>: flex;
}
<span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
    <span class="hljs-attribute">margin</span>: auto;
}
<span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">color</span>: white;
} 
<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.loading</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-comment">/* END Utility Classes */</span>
<span class="hljs-comment">/* Components */</span>
<span class="hljs-selector-class">.bio__media</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-start;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">text-align</span>: left;
}
<span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

}

<span class="hljs-selector-class">.card__custom</span> {
    <span class="hljs-attribute">position</span>: relative;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-between;
}
<span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
    <span class="hljs-attribute">text-align</span>: right;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: space-around;
    <span class="hljs-attribute">overflow</span>: hidden;

}
<span class="hljs-selector-class">.card__custom__img</span> {

    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
    <span class="hljs-attribute">background-position</span>: center;
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">display</span>: inline-block;
    <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
}
<span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-comment">/* END Componenet */</span>
* {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span>{
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

}
<span class="hljs-selector-tag">a</span> {
 <span class="hljs-attribute">text-decoration</span>: none;   
}

<span class="hljs-comment">/* Home Page */</span>
<span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
    <span class="hljs-attribute">font-style</span>: normal;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
    <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-selector-class">.skills_projects_link</span> {
    <span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">margin</span>: auto;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

}

<span class="hljs-comment">/* Header */</span>
<span class="hljs-selector-id">#site_header</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">justify-content</span>: space-between;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-comment">/* Portfolio Page Section */</span>

<span class="hljs-selector-id">#portfolio</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;

}


<span class="hljs-selector-id">#portfolio</span> <span class="hljs-selector-class">.projects</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-around;
}
<span class="hljs-comment">/* Skills */</span>

<span class="hljs-selector-id">#skills_section</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">background-position</span>: top left;
}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
}



<span class="hljs-selector-class">.avatar</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

}


<span class="hljs-selector-class">.card__back</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.rotate__card</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
}
<span class="hljs-comment">/* Site Footer */</span>

<span class="hljs-selector-tag">footer</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
}



<span class="hljs-comment">/* Media Query  */</span>

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
    <span class="hljs-selector-class">.card</span> {
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    }
}
</code></pre>
<p>That's it! We are ready to deply our code to production.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/I6hQnWQU4rQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-continuos-deployment-with-bitbucket-and-netlify">Continuos Deployment with BitBucket and Netlify</h2>
<p>The final step is to deploy our projects so that others can see them. To do that we will use two services:</p>
<ul>
<li>BitBucket, a git-based source code repository for hosting (you can use GitHub if you prefer)</li>
<li>Netlify, a web hosting company that provides hosting for websites that have source code files stored in a Git version control system.</li>
</ul>
<p>You can watch this final video on <a target="_blank" href="https://youtu.be/BH5I68DzcYQ">YouTube here</a>.</p>
<p>You can also checkout the final repositories on BitBucket: </p>
<ul>
<li><a target="_blank" href="https://bitbucket.org/fbhood/simple-twitter/src/master/">SimpleTwitter</a></li>
<li><a target="_blank" href="https://bitbucket.org/fbhood/vue-folio/src">VuePortfolio</a></li>
</ul>
<h3 id="heading-create-the-project-folders-and-copy-all-files">Create the project folders and copy all files</h3>
<p>First, create two folders – one for each project:</p>
<ul>
<li>vue-folio</li>
<li>simple-twitter
then copy all projects files in the related folder.</li>
</ul>
<h3 id="heading-initialize-a-git-repository">Initialize a git repository</h3>
<p>Next, we need to initialise the git repository locally.</p>
<pre><code>cd vue-folio 
git init
git add .
git commit -m<span class="hljs-string">"Initial Commit"</span>
</code></pre><p>You need to execute the commands above in a termial, so you need to have git installed on you system. If you don't, you can read about how to do that <a target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">here</a>.</p>
<p>With the first command, we navigate to the project folder called <code>vue-folio</code>. Then we initialise a git repository, add all files to the staging area, and commit the files.</p>
<p>Repeat the steps above for both projects folders.</p>
<h3 id="heading-create-a-bitbucket-or-github-repository">Create a BitBucket or GitHub repository</h3>
<p>I assume you already have an account with GitHub o BitBucket. But if you don't, then go over and create one.</p>
<p>Follow the steps in the video to create the repositories and connect them with you local repositories.</p>
<h3 id="heading-create-an-account-with-netlify-and-create-a-site">Create an account with Netlify and create a site</h3>
<p>You can use Netlify's free plan for private projects, hobby websites, and experiments. It's a perfect fit for our tutorial. </p>
<p>Follow the steps in the video to deploy your projects there.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/BH5I68DzcYQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-whats-next">What's Next?</h2>
<p>In an upcoming tutorial I'll show you also how to test your code, upgrade to Vue3, and more.</p>
<h3 id="heading-thank-you-for-reading">Thank you for reading!</h3>
<p>I hope you enjoyed this tutorial and the accompanying videos. If so, please share the article and hit the like button on the videos. You can also enable notifications by clicking on the bell icon to know when my next video is online. </p>
<p>If you have any questions please just reach out to me. I reply to all YouTube comments. </p>
<p>Dont forget to subscribe to my YouTube Channel <a target="_blank" href="https://youtube.com/channel/UCTuFYi0pTsR9tOaO4qjV_pQ">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Create a Cross-Platform Twitter Clone with Vue.js ]]>
                </title>
                <description>
                    <![CDATA[ A fun way to learn new programming skills is to create a clone of a popular app. We've released a course that will teach you how to create a Twitter clone using Vue.js, the Quasar framework, and Firebase. Danny Connell, from the Make Apps With Danny ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-cross-platform-twitter-clone-with-vue-js/</link>
                <guid isPermaLink="false">66b2017ca2135cc2539a2154</guid>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Fri, 05 Mar 2021 19:04:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/twitterclone.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A fun way to learn new programming skills is to create a clone of a popular app. We've released a course that will teach you how to create a Twitter clone using Vue.js, the Quasar framework, and Firebase.</p>
<p>Danny Connell, from the Make Apps With Danny channel, created this course. You will learn how to create a beautiful, responsive, cross-platform Twitter app from scratch and get it running and working on 5 different platforms: iOS, Android, Mac, Windows, Web Browser.</p>
<p>Vue.js is a popular JavaScript framework for building user interfaces. The Quasar Framework will allow you to take your Vue.js code and create building apps on multiple platforms with a single codebase.</p>
<p>Here are the sections covered in this course:</p>
<ul>
<li>What We’re Gonna Create</li>
<li>What is Quasar Framework?</li>
<li>Create a Quasar Project</li>
<li>Quasar Folder Structure</li>
<li>Create Layout with Layout Builder</li>
<li>Header</li>
<li>Left Drawer (Navigation)</li>
<li>Right Drawer (Search Bar &amp; News Feed)</li>
<li>Mobile Header</li>
<li>Pages &amp; Routes</li>
<li>About Page</li>
<li>New Tweet Form</li>
<li>Tweets List - Design</li>
<li>Tweets List - Data</li>
<li>Add a Tweet</li>
<li>Delete a Tweet</li>
<li>Animation</li>
<li>Final Design Touches</li>
<li>Firebase - Setup Cloud Firestore</li>
<li>Firebase - Get &amp; Display Tweets</li>
<li>Firebase - Add New Tweet</li>
<li>Firebase - Delete Tweet</li>
<li>Firebase - Update Tweet (Like)</li>
<li>Fix Final Issues</li>
<li>Deploy to Mac (Electron)</li>
<li>Deploy to Windows (Electron)</li>
<li>Deploy to iOS (Cordova)</li>
<li>Deploy to Android (Cordova)</li>
</ul>
<p><strong>Watch the full course on the <a target="_blank" href="https://youtu.be/la-0ulfn0_M">freeCodeCamp.org YouTube channel</a> (2.5 hour watch).</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Send Emails from your Vue.js Application with EmailJS ]]>
                </title>
                <description>
                    <![CDATA[ By Oluwaseyi Bello A few days ago I decided to work on a simple Vue project and needed to send emails through a contact from I had just created. I wanted to receive an automated email every time someone filled out my contact form.  So I got to search... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/send-emails-from-your-vue-application/</link>
                <guid isPermaLink="false">66d4608bbd438296f45cd39f</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 03 Nov 2020 18:23:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/11/EMAIL-JS.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Oluwaseyi Bello</p>
<p>A few days ago I decided to work on a simple Vue project and needed to send emails through a contact from I had just created. I wanted to receive an automated email every time someone filled out my contact form. </p>
<p>So I got to searching and stumbled on <a target="_blank" href="https://www.emailjs.com/">EmailJs</a>. I decided to write this article because I felt their documentation was great and it was really easy to use. I also hope it helps someone out there :)</p>
<h2 id="heading-lets-get-started">Let's Get Started!</h2>
<p>In this article, I will be showing you how to use EmailJS to send emails from your Vuejs application. </p>
<p>Before I continue I am assuming you have Vue CLI installed on your computer as I will be creating a mini demo project with it. If not, you might want to check how to install it <a target="_blank" href="https://cli.vuejs.org/guide/installation.html">here</a>.</p>
<p>We will create the project using this command:</p>
<pre><code>vue create vue-emailjs-demo
</code></pre><p>We’ll then be prompted with the option to pick a default preset or to manually select features. Select <code>**default**</code>.</p>
<p>A new project directory will be created, and you can navigate into it using this command:</p>
<pre><code>cd vue-emailjs-demo
</code></pre><h2 id="heading-how-to-install-emailjs">How to Install EmailJS</h2>
<p>EmailJS helps you send emails using client side technologies only. No server is required – just connect EmailJS to one of the supported email services, create an email template, and use their JavaScript library to trigger an email.</p>
<p>Before we start writing our code, you’ll want to <a target="_blank" href="https://dashboard.emailjs.com/sign-in">create an EmailJS account</a>. With your account, you’ll be able to create email templates and choose the email you want your automated emails to go to.</p>
<p>Once you sign in to your new account, you will be directed to the <a target="_blank" href="https://dashboard.emailjs.com/admin">dashboard</a>.</p>
<h2 id="heading-how-to-create-the-email-template">How to Create the Email Template</h2>
<p>Email templates can optionally contain dynamic variables in almost any field (for example, subject, content, TO email, FROM name, and so on). They are populated from the JavaScript call. We will get into this shortly. </p>
<p>First let's add an email service. I selected Gmail but you are free to select whichever service best suits your needs. </p>
<p>Also, if you don't want to start thinking of a name for your <code>Service ID</code>, tap on the search icon and it will be automatically generated for you.</p>
<p>Next, we’ll create our email template. Navigate to the templates page. Create a new template. Our Email template will define the subject of our email, what content it will contain, where it should be sent, and so on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/emailjdeafault.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The sets of double curly braces shown above like <code>{{from_name}}</code> are variables. When a user fills out our form, we will pass that information to EmailJS using these variables.</p>
<p>Below is a little explanation of the available fields in our template:</p>
<ul>
<li><strong>Subject:</strong> The subject of the email.</li>
<li><strong>Content:</strong> The body of the email. We are going to pass the user message, their name, and their return address here.</li>
<li><strong>To Email:</strong> Contains the destination of this email.</li>
<li><strong>From Name</strong>: This is optional. But you can write your name there.</li>
<li><strong>From Email:</strong> The sender’s email address as it will appear for the recipient. If the default email address checkbox is enabled then EmailJS will use the email address associated with the email service in use.</li>
<li><strong>Reply To:</strong> Sets the email address to which the replies should be sent.</li>
<li><strong>BCC and CC:</strong> These two fields are typically used to send a copy of the message to everyone you’ve listed. <code>**Reply To**</code> , <code>**BCC and CC**</code> won't be used in this guide as we want to keep it as simple as possible. If you need further information, you can check the EmailJS docs <a target="_blank" href="https://www.emailjs.com/docs/tutorial/creating-email-template/">here</a>.</li>
</ul>
<p>Note: At a point in this article, we will be using the <code>Service ID</code> and <code>Template ID</code>. We will also need a <code>User ID</code>. You can find your <code>User ID</code> in the <strong>integration</strong> part of the dashboard. You can copy the details to your clipboard and paste them when they're needed.</p>
<h2 id="heading-how-to-install-emailjs-in-your-application">How to Install EmailJS in your application</h2>
<p>Now moving on to the code :) To install EmailJS in your application, use this command:</p>
<pre><code>npm install emailjs-com --save
</code></pre><p>We’re going to be sending email from a very simple contact form. It will collect data including: name (of the sender), email (of the sender), and the message content. Simple stuff!</p>
<p>You can edit the <code>HelloWorld.Vue</code> component that was created automatically for us when we used the Vue CLI or you can create a fresh component called <code>ContactForm.vue</code>. I will be doing the latter.</p>
<p>Below we will build the contact form component, <code>ContactForm.vue</code>.</p>
<p>Let's start with the <code>template</code>:</p>
<pre><code>&lt;template&gt;
    &lt;div class="container"&gt;
        &lt;form&gt;
          &lt;label&gt;Name&lt;/label&gt;
          &lt;input 
            type="text" 
            v-model="name"
            name="name"
            placeholder="Your Name"
          &gt;
          &lt;label&gt;Email&lt;/label&gt;
          &lt;input 
            type="email" 
            v-model="email"
            name="email"
            placeholder="Your Email"
            &gt;
          &lt;label&gt;Message&lt;/label&gt;
          &lt;textarea 
            name="message"
            v-model="message"
            cols="30" rows="5"
            placeholder="Message"&gt;
          &lt;/textarea&gt;

          &lt;input type="submit" value="Send"&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre><h3 id="heading-explaining-our-markup">Explaining our markup</h3>
<p>As I mentioned earlier, we will be sending email from a very simple contact form. So create a <code>div</code> element which will contain our form content. We will be adding style to our form, so add a class <code>container</code> to the <code>div</code> element.</p>
<pre><code>&lt;style scoped&gt;
* {box-sizing: border-box;}

.container {
  <span class="hljs-attr">display</span>: block;
  margin:auto;
  text-align: center;
  border-radius: <span class="hljs-number">5</span>px;
  background-color: #f2f2f2;
  padding: <span class="hljs-number">20</span>px;
  width: <span class="hljs-number">50</span>%;
}

label {
  <span class="hljs-attr">float</span>: left;
}

input[type=text], [type=email], textarea {
  <span class="hljs-attr">width</span>: <span class="hljs-number">100</span>%;
  padding: <span class="hljs-number">12</span>px;
  border: <span class="hljs-number">1</span>px solid #ccc;
  border-radius: <span class="hljs-number">4</span>px;
  box-sizing: border-box;
  margin-top: <span class="hljs-number">6</span>px;
  margin-bottom: <span class="hljs-number">16</span>px;
  resize: vertical;
}

input[type=submit] {
  background-color: #<span class="hljs-number">4</span>CAF50;
  color: white;
  padding: <span class="hljs-number">12</span>px <span class="hljs-number">20</span>px;
  border: none;
  border-radius: <span class="hljs-number">4</span>px;
  cursor: pointer;
}

input[type=submit]:hover {
  background-color: #<span class="hljs-number">45</span>a049;
}
&lt;/style&gt;
</code></pre><p>You can start your server with the command:</p>
<pre><code>npm run serve
</code></pre><p>Now, open your browser to localhost:8080 and you should see your form:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/vue-form-1.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We will also be creating a method called <code>sendEmail</code> which handles the submission of our data. But before we do that, we need to import <code>emailjs</code> in our file. </p>
<p>Add the following line just under <code>script</code> :</p>
<pre><code><span class="hljs-keyword">import</span> emailjs <span class="hljs-keyword">from</span> <span class="hljs-string">'emailjs-com'</span>;
</code></pre><p>Below is the rest of the code needed in our <code>script</code>:</p>
<pre><code>&lt;script&gt;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'ContactUs'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">''</span>
    }
  },
  <span class="hljs-attr">methods</span>: {
    sendEmail(e) {
      <span class="hljs-keyword">try</span> {
        emailjs.sendForm(<span class="hljs-string">'YOUR_SERVICE_ID'</span>, <span class="hljs-string">'YOUR_TEMPLATE_ID'</span>, e.target,
        <span class="hljs-string">'YOUR_USER_ID'</span>, {
          <span class="hljs-attr">name</span>: <span class="hljs-built_in">this</span>.name,
          <span class="hljs-attr">email</span>: <span class="hljs-built_in">this</span>.email,
          <span class="hljs-attr">message</span>: <span class="hljs-built_in">this</span>.message
        })

      } <span class="hljs-keyword">catch</span>(error) {
          <span class="hljs-built_in">console</span>.log({error})
      }
      <span class="hljs-comment">// Reset form field</span>
      <span class="hljs-built_in">this</span>.name = <span class="hljs-string">''</span>
      <span class="hljs-built_in">this</span>.email = <span class="hljs-string">''</span>
      <span class="hljs-built_in">this</span>.message = <span class="hljs-string">''</span>
    },
  }
}
&lt;/script&gt;
</code></pre><h3 id="heading-what-this-code-is-doing">What this code is doing</h3>
<p>I used a <code>try...catch</code> above but you don't necessarily have to use it. Remember when I asked you to copy your Service ID, Template ID and User ID details to your clipboard and paste them for when they're needed? Well you absolutely need them now! So replace that part of the snippet with your actual details.</p>
<p><code>emailjs.sendForm()</code> is how we send data to EmailJS after passing the Service ID, Template ID, User ID and form data that were passed into <code>sendEmail()</code>. We console.log() any error encountered with the <code>catch()</code> block. </p>
<p>It is important to note that <code>sendForm()</code> sends an email based on the specified email template and the passed form data. So make sure that your form input name is the same as the variable in your EmailJS template.</p>
<p>Below is my modified EmailJS template with the name (of the sender), email (of the sender), and the message content.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/emailjscontactform-1.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>That's it!</p>
<p>Check the <code>To Email</code> address included in your template and you should have your email sent there already. You could also play around with the <strong>Test it</strong> or <strong>playground</strong> feature on the top-right corner of the template if you want.</p>
<h3 id="heading-github-repo">GitHub Repo</h3>
<p>You can find the code for this article in my <a target="_blank" href="https://github.com/Seybel/vue-emailjs-demo">GitHub account</a>.    </p>
<p>Feel free to share this article if you found it helpful. Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an SPA with Vue.js and C# Using .NET Core ]]>
                </title>
                <description>
                    <![CDATA[ Let's say you are a front-end developer. Or you have just had to work more with the front end recently. Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-spa-with-vuejs-and-c-using-net-core/</link>
                <guid isPermaLink="false">66d4604a8812486a37369d15</guid>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 15 Sep 2020 19:14:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/dashboard-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let's say you are a front-end developer. Or you have just had to work more with the front end recently.</p>
<p>Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe you have built a small API with Python.</p>
<p>But you have never touched the modern .NET family tech stack.</p>
<p>This tutorial will guide you, step-by-step, in building a modern single page application (SPA) that will take advantage of <a target="_blank" href="https://vuejs.org/">Vue.js</a> for the front-end and <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/get-started?tabs=windows">.NET Core (C#)</a> for the back-end.</p>
<p>We will also see how to write some tests, both unit and integration, to cover the front and back end functionality (at least partially).</p>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/pizza-app">here</a> ? is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/README.md">README</a> ?.</p>
<h2 id="heading-what-will-we-build">What Will We Build</h2>
<p>?‍? We will build a web app where users can signup/login and just tell us how much they love pizzas, by pressing an "I love it" button.</p>
<p>There are no restrictions on the number of times each user can show us their appreciation. The only requirement is that only logged in users can vote.</p>
<p>On the home page, along with the signup/login buttons, we will put a little bar-chart, displaying the top X users with the highest appreciation (on the X-axis) and the number of votes on the Y-axis.</p>
<h2 id="heading-installation">Installation</h2>
<p>Let's start with the front end. It makes more sense to build the visible parts of our app first. We will build our front end with one of the most famous front-end libraries on the market: Vue.js.</p>
<h3 id="heading-how-to-set-up-the-front-end">How to Set Up the Front End</h3>
<p>In order to install and configure our initial front end setup with Vue.js, we will use the <a target="_blank" href="https://cli.vuejs.org/">Vue CLI</a>. This is a standard command-line tool for developing Vue.js applications.</p>
<p>To install it, use the following command. Note that all commands in this tutorial use <a target="_blank" href="https://www.npmjs.com/">NPM</a>, which you need to have installed on your machine in order to follow along.</p>
<pre><code class="lang-bash">npm install -g @vue/cli
</code></pre>
<p>After successfully installing the Vue CLI, we should be able to start using it to install and configure Vue.js itself. Let's do with the following process.</p>
<p>Step 1: Create a new empty project directory and open it with the following commands:</p>
<pre><code class="lang-python">mkdir pizza-app
cd pizza-app
</code></pre>
<p>Step 2: While in the project root, run the following:</p>
<pre><code class="lang-bash">vue create frontend
</code></pre>
<p>Then from the provided options, select the following:</p>
<ul>
<li>Manual select features</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-52.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - Manually select features</em></p>
<ul>
<li><p>Babel</p>
</li>
<li><p>Router</p>
</li>
<li><p>CSS-Preprocessors (SASS)</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - select features</em></p>
<p>Then select version 2.x from the next screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Select version 2.x</em></p>
<p>Next, select 'Use history mode for router?' and 'Sass/SCSS (with node-sass)', like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Linter / Formatter</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Unit Testing with Jest</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>E2E Testing with Cypress</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-60.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After this last step, finish the installation process with the default options.</p>
<p>Now we are ready to run the app by using the following commands from the root project folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm run serve
</code></pre>
<p>After the app is running, you can see it in your browser on http://localhost:8080:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/welcome-to-your-vuejs-app.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js default installation screen</em></p>
<p>Before going on with building the actual components that our front-end app will have, let's install our back-end app via the .NET Core CLI.</p>
<h3 id="heading-how-to-set-up-the-back-end">How to Set Up the Back End</h3>
<p>As mentioned above, we will use another command line tool, the .NET Core CLI, that gives us the ability to create and configure .NET applications.</p>
<p>If you don't have it already, you may use <a target="_blank" href="https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.401-windows-x64-installer">this link</a> to download it and then install it.</p>
<p>Once you have the .NET Core CLI tool installed, go to your project's root directory and run the following command to create our back-end app. Thus we will create a folder called <code>backend</code> and install a .NET web app inside it:</p>
<pre><code class="lang-python">dotnet new web -o backend
</code></pre>
<h3 id="heading-gitignoreio">gitignore.io</h3>
<p>Before continuing with installing the next packages we will need, let's sort out our <em>.gitignore</em> file.</p>
<p>This is a configuration file that tells <a target="_blank" href="https://git-scm.com/">Git</a> what to ignore when committing changes to Git based repositories (the ones in <a target="_blank" href="https://github.com/">GitHub</a>).</p>
<p>Since we want to have one .<em>gitignore</em> file, it will include rules for two types of applications:</p>
<ul>
<li><p>a Node.js based one, which is our Vue.js front end, and</p>
</li>
<li><p>a .NET (C#) one which is our back end.</p>
</li>
</ul>
<p>For this, we will use a tool called <a target="_blank" href="https://www.toptal.com/developers/gitignore"><em>gitignore.io</em></a><em>.</em> This tool will generate such files for us. The advantage of using this tool is that it allows us to type in what our programming languages/platforms are, and it generates the .gitignore file for us.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Use gitignore.io to generate a .gitignore file</em></p>
<p>Also, at the top of the generated file, it saves links for creation or subsequent editing, as shown below.</p>
<pre><code class="lang-python">
<span class="hljs-comment"># Created by https://www.toptal.com/developers/gitignore/api/webstorm,vue,vuejs,visualstudio</span>
<span class="hljs-comment"># Edit at https://www.toptal.com/developers/gitignore?templates=webstorm,vue,vuejs,visualstudio</span>
</code></pre>
<p>Now we are good to go with the rest of the packages we need to install.</p>
<p>First we will install a package called SpaServices, which will allow us to have our app running via only one URL, and pointing to the .NET app. From its side it will proxy requests to our front-end app.</p>
<p>To install it, open your terminal, go to the <code>backend</code> folder of your project, and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.SpaServices.Extensions --version 3.1.8
</code></pre>
<p>After this, we need to configure our back-end app with the SpaServices package in order to have the desired result.</p>
<p>Every request will go to our .NET back-end app, and if the request is meant for the front end, it will proxy it.</p>
<p>To do this, open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file and update its content to be like this:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Linq;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Builder;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Http;
<span class="hljs-keyword">using</span> Microsoft.Extensions.DependencyInjection;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Configuration;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
    {
        <span class="hljs-keyword">public</span> IConfiguration Configuration { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Startup</span>(<span class="hljs-params">IConfiguration configuration</span>)</span>
        {
            Configuration = configuration;
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to add services to the container.</span>
        <span class="hljs-comment">// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span>
        {
            <span class="hljs-keyword">string</span> connectionString = Configuration.GetConnectionString(<span class="hljs-string">"DefaultConnection"</span>);
            services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlite(connectionString));
            services.AddSpaStaticFiles(configuration: options =&gt; { options.RootPath = <span class="hljs-string">"wwwroot"</span>; });
            services.AddControllers();
            services.AddCors(options =&gt;
            {
                options.AddPolicy(<span class="hljs-string">"VueCorsPolicy"</span>, builder =&gt;
                {
                    builder
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials()
                    .WithOrigins(<span class="hljs-string">"https://localhost:5001"</span>);
                });
            });
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =&gt;
                {
                    options.Authority = Configuration[<span class="hljs-string">"Okta:Authority"</span>];
                    options.Audience = <span class="hljs-string">"api://default"</span>;
                });
            services.AddMvc(option =&gt; option.EnableEndpointRouting = <span class="hljs-literal">false</span>);
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext dbContext</span>)</span>
        {
            <span class="hljs-keyword">if</span> (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors(<span class="hljs-string">"VueCorsPolicy"</span>);

            dbContext.Database.EnsureCreated();
            app.UseAuthentication();
            app.UseMvc();
            app.UseRouting();
            app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); });
            app.UseSpaStaticFiles();
            app.UseSpa(configuration: builder =&gt;
            {
                <span class="hljs-keyword">if</span> (env.IsDevelopment())
                {
                    builder.UseProxyToSpaDevelopmentServer(<span class="hljs-string">"http://localhost:8080"</span>);
                }
            });
        }
    }
}
</code></pre>
<p>? This is the final version of the Startup.cs file and that's why you probably noticed some more stuff in it. We will get back to this a bit later in this tutorial.</p>
<p>At this point, you should be able to run your back-end app. If you wish to do so, run the following commands from the root folder of your project:</p>
<pre><code class="lang-python">cd backend
dotnet run
</code></pre>
<h2 id="heading-how-to-set-up-authentication">How to Set Up Authentication</h2>
<p>As you may remember from the description at the beginning, our app should have a Sign up/Login option.</p>
<p>In order to fulfill this requirement, we will use a 3rd party service called <a target="_blank" href="https://www.okta.com/">Okta</a>. We will install the necessary packages for using the Okta SDK on both the front end and back end of our app.</p>
<p>But before that, you need to create an account on their <a target="_blank" href="https://developer.okta.com/">website</a> and get access to their admin panel. From there you may create a new application. Here is how it looks on mine:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-62.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now let's start with the package we need for our front end. From the root folder, run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm i @okta/okta-vue
</code></pre>
<p>After this step, we are ready to amend our Vue.js app in order to take advantage of Okta SDK.</p>
<p>We will also install another package, called <a target="_blank" href="https://bootstrap-vue.org/">BootstrapVue</a>, that provides a set of good looking and ready to use Vue.js components. This will help us save development time and will also get us a good looking front end.</p>
<p>To install it, run the following from your <code>frontend</code> folder:</p>
<pre><code class="lang-bash">npm i vue bootstrap-vue bootstrap
</code></pre>
<h3 id="heading-how-to-set-up-the-router">How to Set Up the Router</h3>
<p>It's time to do some coding. We need to edit our <a target="_blank" href="https://router.vuejs.org/">router</a> in order to apply what's coming from Okta authentication services.</p>
<p>You can see the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/router/index.js">full file</a> in my GitHub repo, but here is the essential part that you need to configure with your own details (that you got when you registered on the Okta developer website):</p>
<pre><code class="lang-javascript">Vue.use(Auth, {
  <span class="hljs-attr">issuer</span>: <span class="hljs-string">'https://dev-914982.okta.com/oauth2/default'</span>,
  <span class="hljs-attr">client_id</span>: <span class="hljs-string">'0oatq53f87ByM08MQ4x6'</span>,
  <span class="hljs-attr">redirect_uri</span>: <span class="hljs-string">'https://localhost:5001/implicit/callback'</span>,
  <span class="hljs-attr">scope</span>: <span class="hljs-string">'openid profile email'</span>
})

....

router.beforeEach(Vue.prototype.$auth.authRedirectGuard())
</code></pre>
<h3 id="heading-home-screen">Home screen</h3>
<p>After we get our router sorted, we can finally do some changes to our app's home screen, which will actually be visible to our users.</p>
<p>Open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/App.vue">App.vue</a> file in your IDE and change its content as follows:</p>
<pre><code class="lang-javascript">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar</span> <span class="hljs-attr">toggleable</span>=<span class="hljs-string">"md"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"light"</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"light"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-toggle</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-toggle</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-brand</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Love Pizza<span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-brand</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-collapse</span> <span class="hljs-attr">is-nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"login"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!user"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"logout"</span> <span class="hljs-attr">v-else</span>&gt;</span>Logout<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">b-collapse</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        Love pizza button and clicks counter here
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'app'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">user</span>: <span class="hljs-literal">null</span>
    }
  },
  <span class="hljs-keyword">async</span> created() {
    <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
  },
  <span class="hljs-attr">watch</span>: {
    <span class="hljs-string">'$route'</span>: <span class="hljs-string">'onRouteChange'</span>
  },
  <span class="hljs-attr">methods</span>: {
    login() {
      <span class="hljs-built_in">this</span>.$auth.loginRedirect()
    },
    <span class="hljs-keyword">async</span> onRouteChange() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
    },
    <span class="hljs-keyword">async</span> refreshUser() {
      <span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.getUser()
    },
    <span class="hljs-keyword">async</span> logout() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.logout()
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
      <span class="hljs-built_in">this</span>.$router.push(<span class="hljs-string">'/'</span>)
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"scss"</span>&gt;</span>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &amp;.router-link-exact-active {
      color: #42b983;
    }
  }
}
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>By now your app might look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-173.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js front-end app - work in progress</em></p>
<p><strong>Note</strong>: Don't be confused by the text 'Pizza button and clicks counter here'. When building UIs, it's a good practice to leave such placeholders for components that are to be developed in the future.</p>
<p>In our case, this is where we will add the components responsible for the 'I love it' button and the counter later. They will show the number of votes per user.</p>
<pre><code class="lang-javascript">&lt;main&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Love pizza button and clicks counter here
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/main&gt;
</code></pre>
<h3 id="heading-authentication-on-the-back-end">Authentication on the back end</h3>
<p>By now, we have setup our front end to leverage the authentication service provided by Okta. But in order to have the whole picture ready to use, we need to do the same in our back-end app.</p>
<p>This is where we will be doing HTTP calls from, in order to communicate with the database. And, as you will see later, some of these calls will need to be authenticated in order to succeed.</p>
<p>Let's start again with installing some packages that will make our job easier. In your terminal, go to your <code>backend</code> directory and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 3.1.8
dotnet add package Microsoft.Extensions.Configuration --version 3.1.7
</code></pre>
<p>Then we need another package that will help us set up our database. We will use the SQLite database which is super easy to use in .NET Core setup.</p>
<p>While still in the <code>backend</code> folder, run:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 2.1.1
</code></pre>
<p>Once we're finished with the installations, make sure you got the full content of <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file (and if you didn't, do so now).</p>
<h3 id="heading-pizzavotesapiservice">PizzaVotesApiService</h3>
<p>OK everyone, we have set up both our front and back ends to support authentication. We have added SQLite as a database to be used for storing the users votes. And we have an initial version of our home screen.</p>
<p>Now it's time to implement a service on the front end that will allow us to consume our back end's API.</p>
<p>Great job so far! ?</p>
<p>Before being able to make HTTP calls from our front-end app to our back-end app, we need to install one more package in our Vue.js app. It's called <a target="_blank" href="https://www.npmjs.com/package/axios">axios</a> and it gives us the ability to make <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequests</a> from the browser, which is exactly what we need.</p>
<p>Open your terminal, go to the <code>frontend</code> of your project, and run:</p>
<pre><code class="lang-python">npm i axious
</code></pre>
<p>Then, in your IDE, go to the <code>src</code> folder of your front-end app, create a new .js file, and add the following inside it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>

const client = axios.create({
    baseURL: <span class="hljs-string">'http://localhost:5000/api/pizzavotes'</span>,
    json: true
})

export default {
    <span class="hljs-keyword">async</span> execute(method, resource, data) {
        const accessToken = <span class="hljs-keyword">await</span> Vue.prototype.$auth.getAccessToken()
        <span class="hljs-keyword">return</span> client({
            method,
            url: resource,
            data,
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        }).then(req =&gt; {
            <span class="hljs-keyword">return</span> req.data
        })
    },
    getAll() {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, <span class="hljs-string">'/'</span>)
    },
    getById(id) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, `/${id}`)
    },
    create(data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'post'</span>, <span class="hljs-string">'/'</span>, data)
    },
    update(id, data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'put'</span>, `/${id}`, data)
    },
}
</code></pre>
<p>I have named mine <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/PizzaVotesApiService.js">PizzaVotesApiService.js</a>. We will stop with integrating the API for a while and will create another component that will hold the controls the users will use to interact with this API.</p>
<h3 id="heading-dashboard-component">Dashboard Component</h3>
<p>Say hello to our <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Dashboard.vue">Dashboard.vue</a> component.</p>
<p>This is where we will put the 'I love it' button and a small votes counter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>'I love it' button and votes counter</em></p>
<p>We'll also add a nice pizza image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Pizza image</em></p>
<p>As well as a nice bar chart, showing the top X voters.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Top voters bar chart</em></p>
<p>You may copy and paste the full content of the file from my repo so we can continue with integrating the whole thing.</p>
<h3 id="heading-api-integration">API Integration</h3>
<p>I am going to use a small diagram to depict the data flow. As they say, "a picture is worth a thousand words:"</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/data-flow.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Data flow diagram</em></p>
<p>As you can see (I hope ?) from the diagram, when the user enters their votes, the data goes from the dashboard component through the API service we created for communicating with the back-end API. It then reaches the back-end controller that is actually making the HTTP calls.</p>
<p>Once the data is fetched, the service passes it back to our UI where we show it via our Vue.js components. As you will see, there is some additional logic that checks if the user is authenticated in order to know which calls to execute.</p>
<p>Here is the implementation of the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/PizzaVotesController.cs">controller</a> itself:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authorization;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend.Controllers</span>
{
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
    [<span class="hljs-meta">ApiController</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PizzaVotesController</span> : <span class="hljs-title">ControllerBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationDbContext _dbContext;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PizzaVotesController</span>(<span class="hljs-params">ApplicationDbContext dbContext</span>)</span>
        {
            _dbContext = dbContext;
        }

        <span class="hljs-comment">// GET api/pizzavotes</span>
        [<span class="hljs-meta">HttpGet</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;List&lt;PizzaVotes&gt;&gt;&gt; Get()
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.ToListAsync();
        }

        <span class="hljs-comment">// GET api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;PizzaVotes&gt;&gt; Get(<span class="hljs-keyword">string</span> id)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.FindAsync(id);
        }

        <span class="hljs-comment">// POST api/pizzavotes</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPost</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Post</span>(<span class="hljs-params">PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">await</span> _dbContext.AddAsync(model);
            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();
        }

        <span class="hljs-comment">// PUT api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPut(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&gt; <span class="hljs-title">Put</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id, PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">var</span> exists = <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.AnyAsync(f =&gt; f.Id == id);
            <span class="hljs-keyword">if</span> (!exists)
            {
                <span class="hljs-keyword">return</span> NotFound();
            }

            _dbContext.PizzaVotes.Update(model);

            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

            <span class="hljs-keyword">return</span> Ok();
        }
    }
}
</code></pre>
<p>Here we have four methods for executing four basic operations:</p>
<ul>
<li><p>get all records from the database</p>
</li>
<li><p>get the records for one user (by their email address)</p>
</li>
<li><p>create a new user record</p>
</li>
<li><p>update an existing user's records.</p>
</li>
</ul>
<p>You have probably noticed the <code>[Authorize]</code> clause above in three of the four methods. Those methods are going to require the user to be authenticated in order to execute.</p>
<p>We will leave the method <code>GET api/pizzavotes</code> for getting all records unauthorized on purpose. Since we would like to show the bar chart on the home screen to all users, we will need to be able to make this call, no matter if the user is authenticated or not.</p>
<h3 id="heading-allow-registration">Allow registration</h3>
<p>Something to note: if you would like to have a 'Sign Up' on your login screen, you need to allow it from the Okta admin panel.</p>
<p>Once logged in, select from the menu <strong>Users-&gt;Registration</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Allow registration for new users</em></p>
<h3 id="heading-finish-the-back-end">Finish the back end</h3>
<p>In order for your back-end app to become fully functioning, please take a look at <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/tree/master/backend">my repo here</a> and add the missing files.</p>
<p>If you have followed along up to this point you should have the following files (except the <code>test</code> folder, as we are going to add it a bit later):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Back-end app structure</em></p>
<h3 id="heading-finish-the-front-end">Finish the front end</h3>
<p>In order to finalize the work on our front-end app, we will create two more components.</p>
<p>The first one will render the bar chart mentioned above, and the second one will display the email address of the user that is currently logged in.</p>
<p>In your IDE, go to <code>frontend/src/components</code> and create two files, named <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Username.vue">Username.vue</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/VotesChart.vue">VotesChart.vue</a>, respectively.</p>
<p>Username.vue is a very short and simple component that takes the user's email address as a prop and renders it. Here is its implementation:</p>
<pre><code class="lang-python">&lt;template&gt;
  &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">username</span>"&gt;{{ <span class="hljs-title">username</span> }}&lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">template</span>&gt;

&lt;<span class="hljs-title">script</span>&gt;
<span class="hljs-title">export</span> <span class="hljs-title">default</span> {
  <span class="hljs-title">props</span>:</span> { username: String },
}
&lt;/script&gt;

&lt;style lang=<span class="hljs-string">"scss"</span>&gt;
.username {
  color: rebeccapurple;
  display: flex;
  align-items: center;
  justify-content: center;
}

.username::before {
  content: <span class="hljs-string">"•"</span>;
}

.username::after {
  content: <span class="hljs-string">"•"</span>;
}
&lt;/style&gt;
</code></pre>
<p>The only thing to notice here is that we are using SASS/SCSS for the component styles. That is possible because we chose that option in the beginning (when we were installing our Vue.js app).</p>
<p>For drawing the chart we will use a package called <a target="_blank" href="https://www.npmjs.com/package/vue-chartjs">vue-chartsjs</a>. Install it by running the following command from your <code>frontend</code> folder:</p>
<pre><code class="lang-python">npm i vue-chartjs chart.js
</code></pre>
<p>Our component <strong>VotesChart.vue</strong> is going to be kind of wrapper for the bar chart component that comes from the vue-chartjs package.</p>
<p>We use it for getting the data from the parent component, <strong>Dashboard.vue</strong>, and processing it.</p>
<p>We sort the data array in order to display our top voters, sorted from the largest to the smallest number of votes. Then we pass it to the Bar chart component to visualize it.</p>
<p>Here is the full implementation:</p>
<pre><code class="lang-python">&lt;script&gt;
<span class="hljs-keyword">import</span> { Bar } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-chartjs'</span>

const TOP_N_VOTERS_TO_SHOW_ON_CHART = <span class="hljs-number">10</span>

// Used to sort by votes value - <span class="hljs-keyword">from</span> bigger to smaller (desc)
function sortByStartDate(nextUser, currentUser) {
  const nextVoteValue = nextUser.value
  const currentVoteValue = currentUser.value
  <span class="hljs-keyword">return</span> currentVoteValue - nextVoteValue
}

export default {
  extends: Bar,
  props: { data: Array },

  watch: {
    data: <span class="hljs-keyword">async</span> function (newVal) {
      const sortedVotes = Array.<span class="hljs-keyword">from</span>(newVal).sort(sortByStartDate).slice(<span class="hljs-number">0</span>, TOP_N_VOTERS_TO_SHOW_ON_CHART)
      this.labels = sortedVotes.map(user =&gt; user.id)
      this.values = sortedVotes.map(user =&gt; user.value)

      this.renderChart({
        labels: this.labels,
        datasets: [
          {
            label: <span class="hljs-string">'Pizza Lovers'</span>,
            backgroundColor: <span class="hljs-string">'#f87979'</span>,
            data: this.values,
          }
        ]
      }, {
        scales: {
          yAxes: [{
            ticks: {
              stepSize: <span class="hljs-number">1</span>,
              min: <span class="hljs-number">0</span>,
              max: this.values[<span class="hljs-number">0</span>]
            }
          }]
        }
      })
    }
  }
}
&lt;/script&gt;
</code></pre>
<p>Note that there is a constant at the top of the file which will define how many top voters we would like to show on this chart. Currently it is set to 10, but you can amend it as you like.</p>
<p>Once you are ready with all the front-end stuff and want to run the app, you can do so by:</p>
<p>Going to the <code>frontend</code> folder and running:</p>
<pre><code class="lang-bash">npm run serve
</code></pre>
<p>Going to the <code>backend</code> folder and running:</p>
<pre><code class="lang-bash">dotnet run
</code></pre>
<p>Opening your browser and going to https://localhost:5001.</p>
<h2 id="heading-how-to-test-our-applications">How to test our applications</h2>
<p>So far we have built a modern and fully functioning single page application with a .NET Core back end and a SQLite database. That's a lot of work. Congrats! ✨</p>
<p><em>We easily could stop here and go have some cold</em> ?.</p>
<p>But...</p>
<p>We are reasonable enough to know that if we don't test our apps, we can't guarantee that they will work as expected.</p>
<p>We also know that if we want to make our code testable, we should write in a well structured manner, keeping in mind the <a target="_blank" href="https://www.dotnettricks.com/learn/designpatterns/different-types-of-software-design-principles">main principles of software design</a>.</p>
<p>I hope I've convinced you to continue working through this tutorial. After all, the only thing left for us to do is to write some tests for both of our applications. So let's do it!</p>
<p>We will cover the functioning of our back end API with <strong>integration tests</strong>, and for our front end we will write both <strong>unit</strong> and <strong>integration</strong> tests.</p>
<h3 id="heading-unit-and-integration-tests">Unit and integration tests</h3>
<p>? Before going to the code, I would like to say a few words about these types of tests and answer some of most frequently asked questions.</p>
<p>You might be wondering, what are unit tests? What are integrations tests? Why do we need them? When should we use each of them?</p>
<p>Starting from the first question, a <strong>unit test</strong> is a piece of code that tests the functionality of an encapsulated module (meaning another piece). It's written in as a function or some kind of independent block of code.</p>
<p>They are good to have, because they give you quick development time feedback and keep the code safe from regressions when new features are being added.</p>
<p><strong>Integration tests</strong> are useful when we need to test how multiple modules/units are working together. For example a REST API and a database interaction.</p>
<p>Depending on the size of the application we are working on, we might need only integration tests. But sometimes we need both integration and unit tests to bullet proof our code.</p>
<p>Ideally, we should have them both, as they are essential parts of the testing pyramid and are the cheapest to implement.</p>
<p>But in some cases, like for our back-end app, only integration tests are necessary to cover the functionality that is worth being tested. We have only several API methods for working with the database.</p>
<h3 id="heading-how-to-create-our-back-end-tests">How to Create Our Back End Tests</h3>
<p>This time we will start with creating our test solution. To do so, you need to do a few things.</p>
<p>First, install the xUnit library by running the following from your project root directory:</p>
<pre><code class="lang-bash">dotnet add package xUnit
</code></pre>
<p>Then go to your <code>backend</code> folder and create and empty folder called <code>tests</code> . Then inside that folder run:</p>
<pre><code class="lang-bash">dotnet new xunit -o PizzaVotes.Tests
</code></pre>
<p>Once that's done, open <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/backend.csproj">backend.csproj</a> and add the following two lines to the <code>&lt;PropertyGroup&gt;</code> block:</p>
<pre><code class="lang-python">&lt;GenerateAssemblyInfo&gt;false&lt;/GenerateAssemblyInfo&gt;
&lt;GenerateTargetFrameworkAttribute&gt;false&lt;/GenerateTargetFrameworkAttribute&gt;
</code></pre>
<p>Then go to your <code>tests</code> folder and install the following packages:</p>
<pre><code class="lang-python">Microsoft.AspNetCore.Mvc
Microsoft.AspNetCore.Mvc.Core
Microsoft.AspNetCore.Diagnostics
Microsoft.AspNetCore.TestHost
Microsoft.Extensions.Configuration.Json
Microsoft.AspNetCore.Mvc.Testing
</code></pre>
<p>You do that by executing each of the following commands in your terminal app:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Mvc --version 2.2.0
dotnet add package Microsoft.AspNetCore.Mvc.Core --version 2.2.5
dotnet add package Microsoft.AspNetCore.Diagnostics --version 2.2.0
dotnet add package Microsoft.AspNetCore.TestHost --version 3.1.8
dotnet add package Microsoft.AspNetCore.Mvc.Testing --version 3.1.8
</code></pre>
<p>After we have everything installed, we are ready to proceed to actually writing some tests.</p>
<p>As you may see here, except the tests themselves, I have added two more files which we need or are good to have when running the tests.</p>
<p>One of them is just a <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/ContentHelper.cs">helper file</a> with one method for dealing with serialized objects and getting the string content. The other is the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/TestFixture.cs">fixture</a> file, where we have configurations and settings for our test server and client.</p>
<p>And, of course, there's <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/PizzaVotesTests.cs">a file</a> with our tests.</p>
<p>I am not going to paste the content of these files here, as this tutorial has become long enough already.</p>
<p><strong>You can just copy them from my repository.</strong></p>
<p>If you take a closer look at the tests, you might notice that we are testing only the first, not authenticated, call for a success response. The rest we are checking only for 401 HTTP response, which is <code>Unautorized</code>.</p>
<p>That's because only the first method is public, that is, it doesn't need authentication.</p>
<p>If we were to have the same tests for all methods, we would have needed to implement a middleware just to authorize our test app in front of Okta's authentication services.</p>
<p>And since the purpose of this tutorial is to learn a variety of things, we might say it's not worth doing.</p>
<p>Now the fun part: how to run the tests. It turns out to be super simple. Just go to your <code>tests</code> directory (where the tests.sln file is) from your terminal and run:</p>
<pre><code class="lang-bash">dotnet <span class="hljs-built_in">test</span>
</code></pre>
<p>You should see something like this in your terminal (ignore the yellow warnings):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running back-end tests</em></p>
<h3 id="heading-how-to-create-our-front-end-tests">How to Create Our Front-end Tests</h3>
<p>It's time to add some tests to our front end. Here we will do both unit and integration tests.</p>
<p><strong>Unit tests</strong><br>As I mentioned above, unit tests are suitable when we have a module or a component that doesn't have dependencies from the outside world.</p>
<p>Such components turn out to be our Username.vue and VotesChart.vue components.</p>
<p>They are representational components that receive the data they need to function properly via props. This means we can write our tests in the same manner: pass them the data they need and check whether the results of their execution are as expected.</p>
<p>Here's an important thing to mention. It's not that what is provided by the <strong>@vue/test-utils</strong> package (that comes from installing Vue.js), was not enough to test both components.</p>
<p>Rather, for educational purposes, I have decided to install and use the <a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">Vue Testing Library</a> as well. That's why one of the components below is tested with @vue/test-utils, but the other is tested with @testing-library/vue.</p>
<p>Don't forget to install it before running the test:</p>
<pre><code class="lang-python">npm i --save-dev @testing-library/vue
</code></pre>
<p>Again, to save space, I am not going to paste the component test's code here, but you can easily see it <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/Username.spec.js">here</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/VotesChart.spec.js">here</a>.</p>
<p>Then in order to run the unit tests of your front-end app, go to the <code>frontend</code> folder and execute:</p>
<pre><code class="lang-python">npm run test:unit
</code></pre>
<p><strong>Integration test</strong><br>This is probably more interesting for some of you.</p>
<p>If you remember the beginning of this tutorial when we installed our Vue.js app, for our e2e (or integartion) tests solution we selected <a target="_blank" href="https://www.cypress.io/">Cypress.js</a>.</p>
<p>This is a super easy-to-use tool that allows developers to write real e2e tests for their applications by giving them immediate feedback.</p>
<p>From personal experience I would say that working with Cypress is more of a pleasure than with other similar frameworks. If you have previous experience with frameworks like Nightwatch.js or Selenium, what you see below might be familiar to you.</p>
<p>Before running our tests with Cypress, we need to do some minor changes in its configuration.</p>
<p>Find the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/plugins/index.js">index file</a> under the <code>plugins</code> folder and add the following line to the return statement in the end of the file:</p>
<pre><code class="lang-python">  baseUrl: <span class="hljs-string">"https://localhost:5001"</span>
</code></pre>
<p>Now update the content of the test.js under the <code>specs</code> folder as it's shown <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/specs/test.js">here</a>.</p>
<p>Once you have it all done, you should be able to run your e2e tests via Cypress. You can do so by executing the following command while you're in your <code>frontend</code> directory:</p>
<pre><code class="lang-python">npm run test:e2e
</code></pre>
<p>⚡Don't forget to start your back-end app before executing the e2e tests so that they work properly.</p>
<p>If you have followed along, after running the command above you should see something like this in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running e2e tests</em></p>
<p>And a new browser window will be opened by Cyrpess.js itself, where you can use the provided UI to see and run your tests.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-70.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cypress.js UI</em></p>
<p>And when all tests pass, you are supposed to see a screen like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>e2e tests pass successfully</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we have seen how to use one of the hottest technologies on the market, for both the front end and back end.</p>
<p>We've also learned how to combine them properly in order to build a small but fully functioning single page application with database support.</p>
<p>Finally, we also have written unit and integration tests for both ends.</p>
<p>I believe this kind of exercise is beneficial for both experienced and beginner readers, as it covers a lot of different stuff in a step-by-step manner. And you end up with a working example app if you've finished the entire process.</p>
<p>This tutorial ended up being much longer than I initially thought it would be. But if you have done it all, I admire you ?! And I hope it was pleasure for you reading it, as it was for me writing it.</p>
<p>? Thanks for reading! ?</p>
<h2 id="heading-resources">Resources</h2>
<p>You may find below the links that were useful to me in some way while writing this.</p>
<p>https://consultwithgriff.com/spas-with-vuejs-aspnetcore/<br><a target="_blank" href="https://github.com/okta/okta-auth-dotnet">https://github.com/okta/okta-auth-dotnet</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test">https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1">https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1</a><br><a target="_blank" href="https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events">https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events</a><br><a target="_blank" href="https://docs.cypress.io/guides/references/configuration.html#Options">https://docs.cypress.io/guides/references/configuration.html#Options</a><br><a target="_blank" href="https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing">https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing</a><br><a target="_blank" href="https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests">https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests</a><br><a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">https://testing-library.com/docs/vue-testing-library/intro</a><br>https://www.valentinog.com/blog/canvas/</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Integrate ApexCharts with Vue.js – a Detailed Guide ]]>
                </title>
                <description>
                    <![CDATA[ By Oluwaseyi Bello Charts and graphs are a great way to display information/data to your app's users. In this article I will show you exactly how to do that with vue-apexcharts. Getting Started Using the Vue CLI we can easily create a starter applica... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-detailed-guide-to-integrating-apexcharts-with-vuejs/</link>
                <guid isPermaLink="false">66d460893a8352b6c5a2aac7</guid>
                
                    <category>
                        <![CDATA[ charts ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 15 Jun 2020 23:14:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/vueApex-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Oluwaseyi Bello</p>
<p>Charts and graphs are a great way to display information/data to your app's users. In this article I will show you exactly how to do that with vue-apexcharts.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Using the Vue CLI we can easily create a starter application. First, we need to install the Vue CLI with the command below. You can skip this if you have it installed already.</p>
<p>Note: In order to use the CLI, you’ll need to have <a target="_blank" href="https://nodejs.org/">Node.js</a> version 8 or above installed (8.10.0+ is recommended).</p>
<p>To install the CLI, run this command in your terminal:</p>
<pre><code>npm install @vue/cli
</code></pre><p>Now we can use it to create our project. We will create the project using this command:</p>
<pre><code>vue create vue-apexcharts-demo
</code></pre><p>We’ll then be prompted with the option to pick a default preset or to manually select features. Select <code>**default**</code>.</p>
<p>A new project directory will be created, and you can navigate into it using this command:</p>
<pre><code>cd vue-apexcharts-demo
</code></pre><h2 id="heading-installing-apexcharts">Installing Apexcharts</h2>
<p>Before we continue, you might be wondering…</p>
<p><strong>What is apexcharts?!</strong></p>
<p>ApexCharts is a modern charting library that helps developers create beautiful and interactive visualizations for web pages. You can view their demo <a target="_blank" href="https://apexcharts.com/javascript-chart-demos/">here</a>.</p>
<p>We can easily use ApexCharts with our Vue application by integrating its wrapper component for Vue called <strong>vue-apexcharts</strong>.</p>
<p>To install the vue-apexcharts component in our application, use this command:</p>
<pre><code>npm install --save apexcharts 
npm install --save vue-apexcharts
</code></pre><h2 id="heading-configuring-vue-apexcharts">Configuring vue-apexcharts</h2>
<p>Now that we have vue-apexcharts installed we need to install it in our application. Open up the <code>src</code> directory and create a new directory called <code>plugins</code>. Inside the new plugins directory create a file called <code>apexcharts.js</code>.</p>
<p>Next, we will create a Vue component for apexcharts in our <code>apexcharts.js</code>. This will make the component globally available in our application. </p>
<p>To do this, we'll be importing both Vue and vue-apexcharts. Next, we will create a global component called <code>apexchart</code>. Here is what your <code>apexcharts.js</code> file should look like:</p>
<pre><code><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> VueApexCharts <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-apexcharts'</span>

Vue.component(<span class="hljs-string">'apexchart'</span>, VueApexCharts)
</code></pre><h2 id="heading-importing-our-plugin-file">Importing our plugin file</h2>
<p>We have to make Vue aware of the file we just created. To do that, we import it in the <code>main.js</code> file. Open up the main.js file and add the following line after the last import statement:</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-string">'@/plugins/apexcharts'</span>
</code></pre><p>Now we are ready to create our first chart.</p>
<h2 id="heading-creating-our-first-chart">Creating our First Chart</h2>
<p>We will be creating our charts in the HelloWorld component. This component was created automatically when we used the Vue CLI to create our application.</p>
<p>Open up the file <code>HelloWorld.vue</code> and delete all the demo code there.</p>
<p>Your file should look like this:</p>
<pre><code>&lt;template&gt;
&lt;/template&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">  
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
 <span class="hljs-attr">name</span>: <span class="hljs-string">'HelloWorld'</span>
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre><p>Remember in our plugin, we called our component <code>apexchart</code>.</p>
<p>Vue-Apexcharts builds charts by using the data you pass into it using props, as seen in the code below. We will use that to create the HTML for our first chart. In the first example, we will start with a very basic bar chart.</p>
<pre><code>&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">apexchart</span> 
     <span class="hljs-attr">width</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"bar"</span> 
     <span class="hljs-attr">:options</span>=<span class="hljs-string">"options"</span> <span class="hljs-attr">:series</span>=<span class="hljs-string">"series"</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">apexchart</span>&gt;</span>  
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;
</code></pre><p>As you can see in the above template, the <code>apexchart</code> component holds 4 props. Now, let’s jump to the script part.</p>
<pre><code>&lt;script&gt;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'HelloWorld'</span>,
  <span class="hljs-attr">data</span>: <span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">options</span>: {
      <span class="hljs-attr">chart</span>: {
        <span class="hljs-attr">id</span>: <span class="hljs-string">'vuechart-example'</span>
      },
      <span class="hljs-attr">xaxis</span>: {
        <span class="hljs-attr">categories</span>: [
         <span class="hljs-string">"Jan"</span>,
         <span class="hljs-string">"Feb"</span>,
         <span class="hljs-string">"Mar"</span>,
         <span class="hljs-string">"Apr"</span>,
         <span class="hljs-string">"May"</span>,
         <span class="hljs-string">"Jun"</span>,
         <span class="hljs-string">"Jul"</span>,
         <span class="hljs-string">"Aug"</span>,
         <span class="hljs-string">"Sep"</span>,
         <span class="hljs-string">"Oct"</span>,
         <span class="hljs-string">"Nov"</span>,
         <span class="hljs-string">"Dec"</span>
        ]
      }
    },
    <span class="hljs-attr">series</span>: [{
      <span class="hljs-attr">name</span>: <span class="hljs-string">'series-1'</span>,
      <span class="hljs-attr">data</span>: [<span class="hljs-number">55</span>, <span class="hljs-number">62</span>, <span class="hljs-number">89</span>, <span class="hljs-number">66</span>, <span class="hljs-number">98</span>, <span class="hljs-number">72</span>, <span class="hljs-number">101</span>, <span class="hljs-number">75</span>, <span class="hljs-number">94</span>, <span class="hljs-number">120</span>, <span class="hljs-number">117</span>, <span class="hljs-number">139</span>]
    }]
  })
}
&lt;/script&gt;
</code></pre><h2 id="heading-explaining-chart-data">Explaining Chart Data</h2>
<p>Our bar chart contains monthly stock pricing for a fictional organization. We have 12 data points in our bar chart. Each month will be displayed on the x-axis of the chart. Their pricing is displayed on the y-axis of the chart.</p>
<p>To provide the data that will be displayed in our bar chart, we will add a series array. A series is an array of objects. Each object defines the type of chart to be created and will have a data array of values to be plotted on the graph.</p>
<p>You can start your server with the command:</p>
<pre><code>npm run serve
</code></pre><p>Now, open your browser to localhost:8080 and you should see your chart:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/firstapexchart.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-styling-our-charts">Styling our charts</h3>
<p>We can place our chart in the center of the page. To do this I am going to add a class <code>chart-wrapper</code> inside our div. I also want to increase the width of the chart to 800px. My template now looks like this:</p>
<pre><code>&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chart-wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">apexchart</span> 
      <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"bar"</span> 
      <span class="hljs-attr">:options</span>=<span class="hljs-string">"options"</span> <span class="hljs-attr">:series</span>=<span class="hljs-string">"series"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">apexchart</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;
</code></pre><p>Next, let’s add some styling to the new <code>chart-wrapper</code> class that will place our chart in the center of the page. Here is the style I have added:</p>
<pre><code>&lt;style scoped&gt;
div.chart-wrapper {
  <span class="hljs-attr">display</span>: flex;
  align-items: center;
  justify-content: center
}
&lt;/style&gt;
</code></pre><p>While we are adding styles I want to replace the Vue logo with a title. Open up the App.vue file. Delete the <img width="600" height="400" alt="" loading="lazy"> tag and replace it with:</p>
<pre><code>&lt;h1&gt;Vue Apexcharts Demo&lt;/h1&gt;
</code></pre><p>Our charts now looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/firsapexchart.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-adding-a-title-and-changing-chart-color">Adding a Title and Changing Chart Color</h3>
<p>We can add a title to our bar chart to add further context and an explanation of what the chart is all about.</p>
<p>To add a title to our bar chart, go back to your HelloWorld.vue component and add a title to the <code>options</code> object.</p>
<pre><code>options: {
      <span class="hljs-attr">chart</span>: {
        <span class="hljs-attr">id</span>: <span class="hljs-string">'vuechart-example'</span>
      },
      <span class="hljs-attr">xaxis</span>: {
        <span class="hljs-attr">categories</span>: [
         <span class="hljs-string">"Jan"</span>,
         <span class="hljs-string">"Feb"</span>,
         <span class="hljs-string">"Mar"</span>,
         <span class="hljs-string">"Apr"</span>,
         <span class="hljs-string">"May"</span>,
         <span class="hljs-string">"Jun"</span>,
         <span class="hljs-string">"Jul"</span>,
         <span class="hljs-string">"Aug"</span>,
         <span class="hljs-string">"Sep"</span>,
         <span class="hljs-string">"Oct"</span>,
         <span class="hljs-string">"Nov"</span>,
         <span class="hljs-string">"Dec"</span>
        ]
      },
      <span class="hljs-attr">title</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-string">'Monthly Stock Pricing'</span>
      }
 },
 <span class="hljs-attr">series</span>: [{
   <span class="hljs-attr">name</span>: <span class="hljs-string">'series-1'</span>,
   <span class="hljs-attr">data</span>: [<span class="hljs-number">55</span>, <span class="hljs-number">62</span>, <span class="hljs-number">89</span>, <span class="hljs-number">66</span>, <span class="hljs-number">98</span>, <span class="hljs-number">72</span>, <span class="hljs-number">101</span>, <span class="hljs-number">75</span>, <span class="hljs-number">94</span>, <span class="hljs-number">120</span>, <span class="hljs-number">117</span>, <span class="hljs-number">139</span>]
 }]
</code></pre><p>By default, the title is placed on the left side of the bar chart. Here is what our chart looks like now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/firapexchart.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let’s change the default behaviour by adding our own custom style. I want the title to have a bigger font size and to be centered. The title object has a property called <code>align</code> which represents the horizontal plane. I want the title centered on this. To make the title have a bigger font size we need to add a <code>style</code> option.</p>
<p>Also, we can set the bar chart to have a different color. Here is what <code>options</code> look like now:</p>
<pre><code>options: {
      <span class="hljs-attr">chart</span>: {
        <span class="hljs-attr">id</span>: <span class="hljs-string">'vuechart-example'</span>
      },
      <span class="hljs-attr">xaxis</span>: {
        <span class="hljs-attr">categories</span>: [
         <span class="hljs-string">"Jan"</span>,
         <span class="hljs-string">"Feb"</span>,
         <span class="hljs-string">"Mar"</span>,
         <span class="hljs-string">"Apr"</span>,
         <span class="hljs-string">"May"</span>,
         <span class="hljs-string">"Jun"</span>,
         <span class="hljs-string">"Jul"</span>,
         <span class="hljs-string">"Aug"</span>,
         <span class="hljs-string">"Sep"</span>,
         <span class="hljs-string">"Oct"</span>,
         <span class="hljs-string">"Nov"</span>,
         <span class="hljs-string">"Dec"</span>
        ]
      },
      <span class="hljs-attr">title</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-string">'Monthly Stock Pricing'</span>,
        <span class="hljs-attr">align</span>: <span class="hljs-string">'center'</span>,
        <span class="hljs-attr">style</span>: {
          <span class="hljs-attr">fontSize</span>:  <span class="hljs-string">'20px'</span>,
        },
      },
      <span class="hljs-attr">colors</span>: [<span class="hljs-string">'#00897b'</span>]
 },
 <span class="hljs-attr">series</span>: [{
   <span class="hljs-attr">name</span>: <span class="hljs-string">'series-1'</span>,
   <span class="hljs-attr">data</span>: [<span class="hljs-number">55</span>, <span class="hljs-number">62</span>, <span class="hljs-number">89</span>, <span class="hljs-number">66</span>, <span class="hljs-number">98</span>, <span class="hljs-number">72</span>, <span class="hljs-number">101</span>, <span class="hljs-number">75</span>, <span class="hljs-number">94</span>, <span class="hljs-number">120</span>, <span class="hljs-number">117</span>, <span class="hljs-number">139</span>]
 }]
</code></pre><p>This is what the bar chart looks like now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/fiapexchart.png" alt="Image" width="600" height="400" loading="lazy">
<em>Final version of our bar chart</em></p>
<h3 id="heading-creating-a-line-chart">Creating a Line Chart</h3>
<p>First, we need to create a new chart-wrapper div and a new apexchart element. The new apexchart element will hold props like the previous example. But the interesting thing here is all you need to do is to change the prop <code>type</code> in the template to <code>line</code>. </p>
<p>Here is what my HTML code looks like now:</p>
<pre><code>&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>  
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chart-wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">apexchart</span> 
        <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"bar"</span> 
        <span class="hljs-attr">:options</span>=<span class="hljs-string">"options"</span> <span class="hljs-attr">:series</span>=<span class="hljs-string">"series"</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">apexchart</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">hr</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chart-wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">apexchart</span> 
        <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"line"</span> 
        <span class="hljs-attr">:options</span>=<span class="hljs-string">"options"</span> <span class="hljs-attr">:series</span>=<span class="hljs-string">"series"</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">apexchart</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span></span>
</code></pre><p>We now have this line graph:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/fapexchart.png" alt="Image" width="600" height="400" loading="lazy">
<em>Line chart</em></p>
<h2 id="heading-apexcharts-documentation">Apexcharts Documentation</h2>
<p>Apexcharts have different charts type you can choose from apart from bar and line. They also have various options that you can add to your chart. You can add legends or markers, and tooltips for example.</p>
<p>If you want to find out about the other chart types and options that are available you can read their documentation <a target="_blank" href="https://apexcharts.com/docs/installation/">here</a></p>
<h3 id="heading-repo">Repo</h3>
<p>You can find the code for this article in <a target="_blank" href="https://github.com/Seybel/vue-apexcharts-demo">my github account</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Charts helps us visualize data and help users view and interact with it. Integrating ApexCharts to your Vue application is just as easy as you have seen above.</p>
<p>Feedback is welcome! And please share this article if you found it helpful.</p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
