<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Timea Pentek</title>
    <description>The latest articles on DEV Community by Timea Pentek (@pentektimi).</description>
    <link>https://dev.to/pentektimi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1310801%2F50809f24-02df-4d10-8229-1f520c055147.jpeg</url>
      <title>DEV Community: Timea Pentek</title>
      <link>https://dev.to/pentektimi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pentektimi"/>
    <language>en</language>
    <item>
      <title>The process I follow when starting a new project</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Wed, 02 Oct 2024 17:40:19 +0000</pubDate>
      <link>https://dev.to/pentektimi/the-process-i-follow-when-starting-a-new-project-1mh8</link>
      <guid>https://dev.to/pentektimi/the-process-i-follow-when-starting-a-new-project-1mh8</guid>
      <description>&lt;p&gt;As I am starting an exciting project for a small business, I reflected on the steps I follow when initiating a project. One thing I observed is that this process gets more refined as I take on more tasks, and I also pay more attention to having a well-organised plan in place. This helps both me and the client to have a clear timeline and defined expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Gathering requirements for the project
&lt;/h2&gt;

&lt;p&gt;The first step I take is to understand the overall task and the client’s requirements. This is more of a discovery session where I attempt to find out some of the most important aspects of the desired website. For example, for my current project, I learnt that it's important for the client to manage the site and its content independently to some extent. Insights like this often guide my decision on the tech stack I might use.&lt;/p&gt;

&lt;p&gt;After this session, I am able to create a functionality report that provides a clear overview of the project. I then like to reiterate this with the client and dive deeper into more details during a follow-up conversation. During this meeting, I also ask questions about the design or receive it in case it is available. In my situation, I am collaborating with the owner to help develop the design. Once the design is finalised I gather all the necessary resources for the website such as images, vectors, and copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Deciding on the tech stack
&lt;/h2&gt;

&lt;p&gt;Once I gather all the necessary information about the website’s features and functionalities, I like to outline various tech stack options. I then give a short pros/cons presentation to the client, along with my advice for the best approach to take. Next comes the decision on the hosting platform and a conversation about any paid third-party libraries or tools that the front-end might be consuming. This varies depending on the website’s specific functionalities, and the client needs to be aware of any additional costs.&lt;/p&gt;

&lt;p&gt;Since my current client wants to be able to manage the website, I also introduced my recommended content management system when I was presenting the tech stack options. This helps clarify the overall vision, including how it will be possible to manage orders, products, and pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Creating a timeline, setting goals, and developing a plan
&lt;/h2&gt;

&lt;p&gt;The third step in my process is to create a detailed timeline, set goals for the project, and develop a plan. This involves breaking down the project into manageable parts. A well-organised plan keeps both me and the client on track and ensures efficient progress throughout the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Development, testing, and client review
&lt;/h2&gt;

&lt;p&gt;My next step is development itself. By this phase, all the third-party tools, design choices, and assets have been decided on. During this stage, I set up the development environment, after which I implement back-end and front-end functionalities by focusing on modular, reusable elements. I also implement all the features discussed previously with the client.&lt;/p&gt;

&lt;p&gt;Once the website is completed I conduct thorough testing including functionality, usability, and compatibility across browsers and devices. In case any bugs are identified during the testing phase I fix those, after which, I present the website to the client. Following the client review session, I make any necessary adjustments based on the feedback received.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Deployment and maintenance
&lt;/h2&gt;

&lt;p&gt;The last step I take is to prepare for launch by finalising all content and settings and then deploying the website to the live server. I like to do a few more tests once the website is live. My final activities include training the client on how to manage the website and offering ongoing maintenance and support for post-launch issues.&lt;/p&gt;

&lt;p&gt;I found that this approach helps lead to a successful outcome, and that communication is more effective throughout the process. &lt;strong&gt;What are some of the steps you follow when starting a project from scratch?  I would love to find out and learn from a different perspective.&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I made a drag-and-drop file uploader with Vue3 and Firebase</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Sat, 01 Jun 2024 19:16:08 +0000</pubDate>
      <link>https://dev.to/pentektimi/how-i-made-a-drag-and-drop-file-uploader-with-vue3-and-firebase-1697</link>
      <guid>https://dev.to/pentektimi/how-i-made-a-drag-and-drop-file-uploader-with-vue3-and-firebase-1697</guid>
      <description>&lt;p&gt;There is shared responsibility between the client and the server when handling features such as uploading files. The client takes care of transferring the file to the server and providing a way to access the file. The server takes care of validating the upload, storing the file, file permissions, and creating the API for the client to send the file.&lt;/p&gt;

&lt;p&gt;I started out by building the component where users will be able to drop the files. After I was done with the HTML template I had to find a way to ‘pay attention’ to drag and drop actions made by the user. Luckily, there are Javascript event listeners for drag and drop events such as: drag, dragstart, dragend, dragover, dragenter, dragleave, drop. I added these event listeners to the div tag that acted as the ‘drop zone’, however, by default most browsers redirect the user away from the app when a file is dropped. To avoid this behaviour I used event modifiers, which allowed me to handle what happens when a file is dropped. The event modifiers I added were ‘prevent’ and ‘stop’. Once the drop event fired I created an upload function where I handled most of the logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
   &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w-full px-10 py-20 rounded text-center cursor-pointer border border-dashed border-gray-400 text-gray-400 transition duration-500 hover:text-white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{ 'bg-[#4EE4A2] border-[#4EE4A2] border-solid': is_dragover }&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;dragstart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;dragend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_dragover = false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;dragover&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_dragover = true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;dragenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_dragover = true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;dragleave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;is_dragover = false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;upload($event)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step was to retrieve the file/s that the user uploaded. To do this, I needed access to the $event object. The event object refers to the argument added to the event handler function, and it contains information regarding the event that caused the given handler to be invoked. The event object contains a property called ‘dataTransfer’, which is an object that contains the information on the files transferred.&lt;/p&gt;

&lt;p&gt;Once I had access to the uploaded files the next step was to transfer them to the Firebase storage. Since the event object provided me with the files in an object format (this was tricky as the object has numeric keys and it looked like an array), the first step I took was to convert them into an array so that I can loop through the array and send each file to Firebase. I converted the object into an array using the spread operator, and iterated through the array. For each file I added a validation to check whether the file’s mime type corresponds to what I was expecting to receive from the user. Validation can be performed both on the client and server side. For this particular project I was allowing only mp3 files to be sent to the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is_dragover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="c1"&gt;// convert obj to array&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTransfer&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTransfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audio/mpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I initialised the Firebase storage and set up a reference to the main storage which basically represents the bucket url. Once I had reference to the main storage I also created a child reference to the folder holding the audio files just to keep everything clean. The last step was to actually upload the files to Firebase, and to achieve this I used a Firebase specific function called uploadBytesResumable(ref, data, metadata). I choose this function because it exposes progress updates, which helped me create progress bars for the uploads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is_dragover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="c1"&gt;// convert obj to array&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTransfer&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTransfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audio/mpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storageReference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;podcastRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storageReference&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`/podcasts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uploadBytesResumable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;podcastRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is roughly the logic behind how I managed to upload files to Firebase that came from a drag and drop event. I later added to the project animated progress bars, handled the responses from Firebase accordingly and stored the file data in Firebase database with additional fields such as who uploaded the audio file, the name of the file, genre, etc. Follow this link to see the full component logic: &lt;a href="https://github.com/PentekTimi/podcast-listener/blob/master/src/components/AppUpload.vue" rel="noopener noreferrer"&gt;https://github.com/PentekTimi/podcast-listener/blob/master/src/components/AppUpload.vue&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Quick overview of reactivity in Vue3</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Thu, 30 May 2024 21:48:16 +0000</pubDate>
      <link>https://dev.to/pentektimi/quick-overview-of-reactivity-in-vue3-3cbh</link>
      <guid>https://dev.to/pentektimi/quick-overview-of-reactivity-in-vue3-3cbh</guid>
      <description>&lt;p&gt;In my opinion, one of Vue’s most powerful features is its reactivity system. Reactivity allows the framework to automatically update the UI when the information behind it changes. By default, Javascript is not naturally reactive, if nobody tells it, it won’t know that the state changed.&lt;/p&gt;

&lt;p&gt;In Vue3 with the Composition API it is common to use ref() and reactive() to add reactivity to a variable/object. Under the hood, Vue converts the data into a proxy object, which enables Vue to perform dependency tracking and change-notification when properties are accessed or modified. In-depth blog about reactivity here: &lt;a href="https://blog.logrocket.com/your-guide-to-reactivity-in-vue-js/%C2%A0" rel="noopener noreferrer"&gt;https://blog.logrocket.com/your-guide-to-reactivity-in-vue-js/ &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most common ways in Vue Composition API to achieve reactive states are by using reactive() or ref().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;reactive()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This function only works with objects, and it does not work with primitives. The returned value from reactive() is a Proxy of the original object. When implementing reactive() we can avoid using the ‘.value’ to retrieve the properties of the object, in contrast with the ref() function where we have to use ‘.value’. To access a property we can do so by using ‘object.key’ notation. &lt;/p&gt;

&lt;p&gt;The question is, however, if losing ‘.value’ is a benefit or not. I have seen many opinions that losing ‘.value’ can make it harder to track which line of code is triggering a reactive effect. This issue is not so noticeable with small projects, but it can become much more difficult to track reactive objects in a large project. In fact, Vue dropped the reactivity transform feature which got rid of the ‘.value’ when using ref() mainly for this reason.&lt;/p&gt;

&lt;p&gt;In addition to this, reactive has a few limitations such as limited value types (only works with objects), can’t replace entire reactive objects because the reactivity connection to the first reference would get lost, and is not destructure-friendly because, again, reactivity connection would get lost.&lt;/p&gt;

&lt;p&gt;Because of these limitations, Vue currently recommends using ref as the primary API to declare a reactive state. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ref()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ref works with any data type, including objects. To create a reactive value for primitives (boolean, string, number, etc) we can only use ref(). Whenever we want to access the current value of the reactive property we have to use ‘(name of the value/object).value’. You do not need to append the ‘.value’ when using the ref in the HTML template. With ref, if you are passing it an object you can completely replace it with another one, this is not possible with reactive.&lt;/p&gt;

&lt;p&gt;Hopefully, with this quick overview you've gained a bit more clarity about the differences of the two.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Keeping track of the authentication state with Pinia in a Vue.js web app</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Tue, 30 Apr 2024 13:52:48 +0000</pubDate>
      <link>https://dev.to/pentektimi/keeping-track-of-the-authentication-state-with-pinia-in-a-vuejs-web-app-3gb3</link>
      <guid>https://dev.to/pentektimi/keeping-track-of-the-authentication-state-with-pinia-in-a-vuejs-web-app-3gb3</guid>
      <description>&lt;p&gt;Once a user is registered, or logged in in most cases the dashboard or navigation links will slightly change in an application to reflect the logged-in state. This was something that during my project I also wanted to achieve. To keep track of the authentication state I used Pinia state management and created a user store. By doing this I was able to conditionally render different components/html elements or content depending on the authentication state.&lt;/p&gt;

&lt;p&gt;The first step was to create a Pinia store and initialise the authentication state as false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pinia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userLoggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a previous blog post, I described how I registered users using Firebase (link to the blog post: &lt;a href="https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36"&gt;https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36&lt;/a&gt;), however, this solution was isolated to a single component. I wanted to have easy access to the register function and I wanted to keep track of the authentication state. &lt;/p&gt;

&lt;p&gt;This is where Pinia's actions property came in handy. Pinia actions are functions that outsource business logic, they become available to all components. Actions can be asynchronous and can commit multiple mutations which helps reduce the amount of code you have to write. &lt;/p&gt;

&lt;p&gt;I decided to extract the register function logic from the component and add it to Pinia actions, as this function plays a role in updating the authentication state. Once a user registered I wanted to give access to their personal dashboard and update the authentication state to true. I imported the necessary methods from Firebase and references to the Auth instance and Firestore database (find details on this here: &lt;a href="https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36"&gt;https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36&lt;/a&gt;). After initialising the ‘userLoggedIn’ as false, I created an action function called register. To be able to access the ‘this’ keyword this function has to be written as a regular function, and not an arrow function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pinia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/includes/firebase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createUserWithEmailAndPassword&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDoc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userLoggedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// logic to register user&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userCred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userCred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="c1"&gt;// logic to update state&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code block above you can see that after the logic of registering a user, I updated the authentication state using the ‘this’ keyword. The keyword refers to the ‘userLoggedIn’ variable. The asynchronous function register receives as an argument the values object which contains the input data that the user entered.&lt;/p&gt;

&lt;p&gt;In the component that has the register form, I replaced the register logic inside the try block with the action function imported from the Pinia user store. To be able to access this function I used the mapActions method from Pinia. The script looked something like this after the changes were made.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mapActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pinia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useUserStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/stores/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppRegister&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;mapActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useUserStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you are wondering where the 'values' argument comes from, I used VeeValidate to handle the form. In VeeValidate if you add a submit handler to the vee-form component, VeeValidate will automatically pass the form values to the handler as the first argument.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Authentication with Firebase for a Vuejs project</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Tue, 30 Apr 2024 13:41:53 +0000</pubDate>
      <link>https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36</link>
      <guid>https://dev.to/pentektimi/authentication-with-firebase-for-a-vuejs-project-5h36</guid>
      <description>&lt;p&gt;In my most recent Vuejs project, I had to decide how I would be going about the authentication feature of the app. Authentication is a task that is mainly left to the back-end/server, however, the browser or client does play a role in it too. Vue.js is a client-side framework, so I needed a server that could handle authentication. I opted for Firebase as a back-end solution and decided to store the user data in the Cloud Firestore. &lt;/p&gt;

&lt;p&gt;Firebase can take care of hashing passwords, sending confirmation emails, or storing the user’s data. The role of the front end during authentication is relatively simple. The Vue application sends the register/login details to the server, Firebase will then take care of the authentication and check if the user exists with the credentials that were sent. &lt;/p&gt;

&lt;p&gt;In case the authentication is successful, the server would respond with a token, and then this can be stored. It is important to store the token because it can be sent back to the server whenever data related to the user has to be modified or updated. Tokens are unique and only sending back the token to the server will suffice; we won’t have to send back all the authentication data. Firebase will not actively keep track of who is logged in, instead, we use the tokens to verify the user. This type of authentication is known as stateless authentication.&lt;/p&gt;

&lt;p&gt;In this blog post I will be describing the steps I took that resulted in authenticating users successfully. As a quick overview, I first set up the Firebase SDK (software development kit) and initialised the app, then handled user registration with the help of ‘createUserWithEmailAndPassword’ method from Firebase. &lt;/p&gt;

&lt;p&gt;The Firebase SDK is a library that helps us communicate with Firebase’s products, so the first step I took was to install the SDK and initialise Firebase.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install firebase&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Configuring and initialising Firebase can be done in the main.js file, however, to avoid cluttering the app, I decided to separate this into a different file. Instead of having all the configuration code in the main.js, I just had to import the file. I am using the Firebase modular API, which is designed to make your web app as small as possible by importing functions individually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//path to file: src/includes/firebase.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;//configuration object&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;us&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Firebase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="nx"&gt;where&lt;/span&gt; &lt;span class="nx"&gt;we&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;authentication&lt;/span&gt; &lt;span class="nx"&gt;information&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;describes&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="nx"&gt;where&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//initialise the app&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also imported the authentication module for the authentication services to be available. The authentication service of Firebase stores only the email and password, but it can’t hold any additional data such as name, age, or country. I added an input field requiring the entry of the user’s name to my registration form. To be able to store the name of the user I used the Firebase database (Firestore) service.&lt;/p&gt;

&lt;p&gt;I created references to the authentication and Firestore services in the firebase.js file. I did this to not import and call repeatedly the same functions in multiple components. I also had multiple collections in Firestore, so I created a reference to the collection that held the user information. Below you can find the necessary imports and the final version of the firebase.js file.&lt;br&gt;
Note: for all of this to work I had to enable the authentication services within the Firebase console, and made a few changes to the Cloud Firestore rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;initializeFirestore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;persistentLocalCache&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;initializeFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;localCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;persistentLocalCache&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;//reference to authentication service&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//reference to the firestore service&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;//reference to user collection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;usersCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I handled the registration logic. I navigated to the component that has the registration form and listened to a submit event on the form. Once the event was triggered I wanted to call an asynchronous register function, which had a parameter that contained the input values (data from the input fields such as email, name, and password). &lt;/p&gt;

&lt;p&gt;The createUserWithEmailAndPassword function from Firebase accepts as arguments three elements, the auth instance, the user’s email, and the user’s password. Below you can see that I created the user with this function. Now that the user has been created, Firebase generates a unique user identifier (User UID) for it. I wanted this user identifier to be the document name in the 'users' collection, so I passed the UID to the doc() function. The doc() function gets a document reference instance that refers to a document at the specified path, the arguments passed to this function are the path segments. The setDoc() function writes to the document referred to by a document reference, and if the document does not exist in that case it will create it. The parameters that the setDoc() function accepts are the document reference and a map of the fields and values to be added. I wrapped this logic in a try-catch block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;   &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userCred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createUserWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userCred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;Unexpected&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, the code is isolated to a single component, if you would want to register a user at a different location in your application it would make sense to refactor the code using Pinia actions.&lt;/p&gt;

&lt;p&gt;Once a user is registered, or logged it is also very common that the dashboard or navigation links will slightly change to reflect the logged-in state. This was something that during my project I also wanted to achieve. I used Pinia state management and created a user store to keep track of the authentication state. I refactored the register function and added it to a Pinia store’s action function. I rendered conditionally different components/HTML elements or content in case the user was logged in.&lt;/p&gt;

&lt;p&gt;Check out the refactored code in this blog post: &lt;a href="https://dev.to/pentektimi/keeping-track-of-the-authentication-state-with-pinia-in-a-vuejs-web-app-3gb3"&gt;https://dev.to/pentektimi/keeping-track-of-the-authentication-state-with-pinia-in-a-vuejs-web-app-3gb3&lt;/a&gt;, or find the full repository here: &lt;a href="https://github.com/PentekTimi/podcast-listener" rel="noopener noreferrer"&gt;https://github.com/PentekTimi/podcast-listener&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Vue.js lifecycle hooks</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Sun, 28 Apr 2024 12:42:33 +0000</pubDate>
      <link>https://dev.to/pentektimi/vuejs-lifecycle-hooks-50fn</link>
      <guid>https://dev.to/pentektimi/vuejs-lifecycle-hooks-50fn</guid>
      <description>&lt;p&gt;Today I strengthened my knowledge of &lt;em&gt;Vue.js lifecycle hooks&lt;/em&gt; to be able to correctly unmount a component and stop any requests to the server that would be ongoing in the background. This is a situation I came across while building a file upload component in my &lt;em&gt;Vuejs project&lt;/em&gt;, where I had to cancel the upload/s in case a user navigated away from the page. &lt;/p&gt;

&lt;p&gt;It was a very important step to take care of as components or resources not cleaned up properly could have impacted the performance of the application, but also cause excessive memory usage both to the server and front-end.&lt;/p&gt;

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

&lt;p&gt;Vue.js and many other front-end frameworks offer predefined methods that allow us to get hold of a specific phase of the component’s lifecycle. Some of the Vuejs hooks are beforeCreate, created (creation hooks), beforeMount, mounted (mounting hooks), beforeUpdate, updated (updating hooks), beforeUnmount, and unmounted (destruction hooks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are lifecycle hooks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vuejs lifecycle hooks are predefined methods that get executed in a certain order throughout the lifecycle of a component from the initialisation of that Vue instance to its destruction. The lifecycle hooks allow you to know when a component is created, added to the DOM, updated, or destroyed.&lt;/p&gt;

&lt;p&gt;There are some differences in how to use the lifecycle hooks in Vue 3 Composition API vs. Option API. &lt;strong&gt;Creation hooks&lt;/strong&gt; are the first thing that runs in our component, however in the Composition API both creation hooks are replaced by the setup() method. In the Options API, we have the beforeCreate and the created hooks. When using Options API we don’t have to import the lifecycle hooks, however, when using Composition API we have to import the hook before we can use it; this is to keep the project as lightweight as possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;beforeCreate&lt;/strong&gt;:&lt;br&gt;
The beforeCreate hook runs at the initialisation of the component. It runs before we have any reactive elements or DOM elements. At this stage, we don’t have access to any data or events, and we can’t call component methods either. &lt;br&gt;
As I was trying to find use cases for this lifecycle hook I tended to come across information suggesting that this hook is not extensively used. However, it can be useful when we need to make an API call that does not need to be assigned to data (as the assigned data would be lost once the state is initialised), or for actions that require low-level setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;created&lt;/strong&gt;:&lt;br&gt;
This is the first hook that gives us access to our component's data and events. The DOM is yet to be mounted and rendered, and the $el property will not be available yet.&lt;br&gt;
This is one of the most used lifecycle hooks and is a perfect place to fetch external data.&lt;br&gt;
The hook is useful when we want to read/write the reactive data, for example making an API call and storing the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mounting hooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The mounting hooks run when the DOM is mounted, and they handle rendering the component. The hooks are commonly used in applications, as they allow us to access the component right before and after the first render.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;beforeMount&lt;/strong&gt;:&lt;br&gt;
The beforeMount hook runs right before the component DOM is rendered and mounted, here the template and the scoped styles are compiled. The root element (this.$el) does not exist yet, so we can’t manipulate the DOM. It is important to note that this hook is not called during server-side rendering. The hook is useful for performing actions before the DOM is updated and should be the last stage to make API calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mounted&lt;/strong&gt;:&lt;br&gt;
In the mounted hook, we have access to the reactive components and rendered DOM. In the Options API, we can access the DOM via the ‘this.$el’, and in the Composition API we need to use refs. At this stage the app component in the project becomes functional and the data is fit into the template, so it can be manipulated. The mounted hook is commonly used to set up event listeners, modify the DOM, or fetch external data. It is important to note that this hook is not called during server-side rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating hooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The update hooks run when reactive data is modified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;beforeUpdate&lt;/strong&gt;:&lt;br&gt;
The beforeUpdate hook runs after data changes, but right before the DOM is patched and re-rendered. This is a good stage to update the DOM manually or for any logic before data changes, like removing event listeners. It is also safe to mutate the component state at this stage. Note that this hook does not run during server-side rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;updated&lt;/strong&gt;:&lt;br&gt;
The updated hook runs after data changes on your component and the DOM re-renders and updates. A parent component's updated hook runs after that of its child components. Avoid mutating component state during this hook as that would likely cause an infinite loop. You can access the updated DOM during this hook. Note that this hook is not called during server-side rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Destruction hooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The destruction hooks are used in the process of removing a component. They are great for clean-ups to avoid memory leaks, and sending analytics. Destruction hooks are not called during server-side rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;beforeUnmount&lt;/strong&gt;:&lt;br&gt;
The beforeUnmount is invoked right before teardown or destruction. At this stage, the component still completely exists and is fully functional, as it has not been destroyed yet. This hook is most used to clean up components, such as removing event listeners or clearing timeouts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;unmounted&lt;/strong&gt;:&lt;br&gt;
The destroyed hook is when everything part of the component has been torn down, or destroyed from existence. At this stage all associated reactive effects have been stopped, including render effects and computed watchers. This method is also useful for cleanup required within the component, such as stopping server connections.&lt;/p&gt;

&lt;p&gt;The lifecycle hooks described above are the main Vue lifecycle hooks, but there are some additional ones. To learn about these, follow this link: &lt;a href="https://vuejs.org/api/options-lifecycle.html" rel="noopener noreferrer"&gt;https://vuejs.org/api/options-lifecycle.html&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Displaying multiple error messages on incorrect form entries with Vee-validate | Vue</title>
      <dc:creator>Timea Pentek</dc:creator>
      <pubDate>Thu, 25 Apr 2024 21:59:52 +0000</pubDate>
      <link>https://dev.to/pentektimi/displaying-multiple-error-messages-on-incorrect-form-entries-with-vee-validate-vue-367f</link>
      <guid>https://dev.to/pentektimi/displaying-multiple-error-messages-on-incorrect-form-entries-with-vee-validate-vue-367f</guid>
      <description>&lt;p&gt;A typical interaction with websites involves filling in forms. Form validation and displaying errors on incorrect input entries will be a task to account for when building a webpage.&lt;/p&gt;

&lt;p&gt;Form validation can be done both on the client side and the server side. Client-side validation is an initial check that happens in the browser before the data is sent to the server. It ensures that the requirements set are fulfilled by the data that is being entered, and it provides the user with an instant response in case an entry is incorrect. &lt;br&gt;
According to MDN (&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation&lt;/a&gt;), client-side validation is not an exhaustive security measure as it is easy to by-pass, and apps should always perform security checks on data submitted on the server-side as well. Server-side validation is a check that occurs on the server after the data is submitted. Before the data is saved to the server it has to pass a server-side validation. If the data doesn’t pass the check, a response is sent back to the user with the corrections that should be made.&lt;/p&gt;

&lt;p&gt;In this blog post I am exploring the &lt;strong&gt;client-side form validation&lt;/strong&gt; with &lt;strong&gt;Vee-validate&lt;/strong&gt; (&lt;a href="https://vee-validate.logaretm.com/v4/" rel="noopener noreferrer"&gt;https://vee-validate.logaretm.com/v4/&lt;/a&gt;) and &lt;strong&gt;vee-validate/rules&lt;/strong&gt; (&lt;a href="https://www.npmjs.com/package/@vee-validate/rules" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@vee-validate/rules&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This blog post reviews the solution I implemented to destructuring the error message for a form in a &lt;strong&gt;Vue 3&lt;/strong&gt; application.&lt;/p&gt;

&lt;p&gt;When an input field is being validated against multiple rules, what are the options for sending back an error response to the user, assuming the user added an incorrect value in the input field? Should we send back a single error message or do we destructure the error message and send back multiple descriptive error messages for each rule that failed?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ol63fdzo5kjfd1zf82n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ol63fdzo5kjfd1zf82n.jpg" alt="single error message versus multiple descriptive error messages" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It probably depends on the input field and what type of data is being asked, however generic messages such as ‘An error occurred’ lack context, and so we should always try to provide the user with enough details.&lt;/p&gt;

&lt;p&gt;The password field is a common place where the user is required to meet multiple rules, and in my &lt;strong&gt;Vue application&lt;/strong&gt; the input is validated against multiple criteria. Because of this, I decided to display multiple error messages for the password field, which is part of a registration form built with &lt;strong&gt;Vee-validate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt; did I decide to display multiple error messages for the password field? The main reason behind my decision was &lt;strong&gt;user experience (UX)&lt;/strong&gt;. It can become annoying for the user to try and guess what went wrong and why the field is not accepting their input.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's get started!
&lt;/h2&gt;

&lt;p&gt;To get started, I created a basic &lt;strong&gt;Vue 3 app&lt;/strong&gt;. In the components folder, I generated a new file called AppForm.vue for the form template. I then added the AppForm component to the main App.js file.&lt;/p&gt;

&lt;p&gt;Below is the form template that has three input fields and a submit button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Email --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; 
      &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your email"&lt;/span&gt; 
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Password --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; 
      &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Create password"&lt;/span&gt; 
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Confirm Password --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"confirm_password"&lt;/span&gt; 
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Confirm password"&lt;/span&gt; 
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Submit Btn --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; 
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; 
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block bg-[length:400px_200px] mt-6 w-52 bg-gradient- to-r from-[#1F1C2C] via-[#928DAB] to-[#1F1C2C] text-white py-1.5 px-3 rounded enabled:hover:bg-[right_center] enabled:transition-all enabled:duration-500"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Submit
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the &lt;strong&gt;Vee-validate library&lt;/strong&gt;, run in the terminal the following: &lt;br&gt;
&lt;code&gt;npm i vee-validate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In my project I also used &lt;strong&gt;TailwindCSS&lt;/strong&gt;, just to be able to quickly style the form. I installed the necessary resources and made the configurations to set up Tailwind.&lt;/p&gt;

&lt;p&gt;Next, I wanted to configure the Vee-validate library. To not clutter the App.js I decided to outsource this to a validation.js file that I created in a new folder called plugins, inside the src folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// path: src/plugins/validation.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the configuration file, I am creating the Vee-Validate plugin. In Vue, plugins are objects with a method called install. When we register the plugin Vue will call the install method.&lt;br&gt;
To register the plugin we have to add the following to our main.js file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VeeValidatePlugin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./plugins/validation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VeeValidatePlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Your app may import other files such as CSS stylesheets.&lt;/p&gt;

&lt;p&gt;The reason why I chose to set up VeeValidate this way is because the project I am working on has other forms throughout the app. This way VeeValidate will be available globally for me.&lt;br&gt;
Next, I installed the vee-validate/rules package that includes the most common validators for input fields (&lt;a href="https://www.npmjs.com/package/@vee-validate/rules" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@vee-validate/rules&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @vee-validate/rules&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;VeeValidate handles complex validations in an easy way. It uses the &lt;strong&gt;Vee-form&lt;/strong&gt;, &lt;strong&gt;Vee-field&lt;/strong&gt;, and &lt;strong&gt;ErrorMessage&lt;/strong&gt; components to validate forms. In the configuration file, validation.js, I imported these components. After, I imported the validation rules and defined them using the defineRule function from vee-validate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//import the components and defineRule function from vee-validate&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Form&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;VeeForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Field&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;VeeField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vee-validate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// import the validation rules from vee-validate/rules package&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;not_one_of&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vee-validate/rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Make components available globally in the Vue application using the app.component() method. &lt;/span&gt;
     &lt;span class="c1"&gt;// The function takes the arguments: app.component("registered name", implementation)&lt;/span&gt;

&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VeeForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VeeForm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VeeField&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VeeField&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ErrorMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ErrorMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// Define the rules. The defineRule function accepts a rule name that acts as an identifier &lt;/span&gt;
     &lt;span class="c1"&gt;// for that validation rule, the second argument is the validator function that will verify the field value.&lt;/span&gt;

&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;max&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;passwords_mismatch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="nf"&gt;defineRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;excluded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration for VeeValidate is complete for now. To implement it, I had to make a few changes to the form template in the AppForm.vue component. The first changes that I made were the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing the form tag to vee-form&lt;/li&gt;
&lt;li&gt;Changing the input tags to vee-field. Note: Vee-field will by default render an input tag. In order to render a different field add the ‘as’ prop to tell the component which field it should render (eg. as=’select’)&lt;/li&gt;
&lt;li&gt;Adding an ErrorMessage component to each input. The name attribute on the ErrorMessage component has to match the name attribute on the vee-field. This is how to tell vee-validate for which input field should the ErrorMessage display the messages for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The vee-validate rules can be added in two different ways, either by adding the rule directly to the input field or by outsourcing the rules to an object. I opted for outsourcing the rules to a schema object to keep the form as clean as possible. To achieve this I used the validation-schema prop on the vee-form and passed in an object called schema. With all these changes my form looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;vee-form&lt;/span&gt; &lt;span class="na"&gt;:validation-schema=&lt;/span&gt;&lt;span class="s"&gt;"schema"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Email --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;vee-field&lt;/span&gt; 
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; 
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter your email"&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; 
       &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ErrorMessage&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[#CF6679]"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Password --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;vee-field&lt;/span&gt; 
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; 
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; 
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Create password"&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; 
       &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ErrorMessage&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[#CF6679]"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Confirm Password --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;vee-field&lt;/span&gt; 
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"confirm_password"&lt;/span&gt; 
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; 
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Confirm password"&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt; 
       &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ErrorMessage&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[#CF6679]"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"confirm_password"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Submit Btn --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; 
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; 
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block bg-[length:400px_200px] mt-6 w-52 bg-gradient-to-r from-[#1F1C2C] via-[#928DAB] to-[#1F1C2C] text-white py-1.5 px-3 rounded enabled:hover:bg-[right_center] enabled:transition-all enabled:duration-500"&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Submit
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/vee-form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I filled out the schema object inside the AppForm.vue component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AppForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required|email|min:3|max:100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required|min:7|max:50|excluded:password,qwerty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="na"&gt;confirm_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;passwords_mismatch:@password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add the rules to the schema object, you create key-value pairs, where the key is the name attribute on the input, and the value is the rule/s. The form has 3 input fields or vee-fields, so I have three key-value pairs in the schema object. Each rule has to be separated by a vertical line ‘|’. &lt;/p&gt;

&lt;p&gt;Below are a few short descriptions of what each rule means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Required: field has to be filled out&lt;/li&gt;
&lt;li&gt;Email: input value has to be of type email&lt;/li&gt;
&lt;li&gt;Min: the minimum amount of characters a value has to have &lt;/li&gt;
&lt;li&gt;Max: the maximum amount of characters a value can have&lt;/li&gt;
&lt;li&gt;Excluded: (the validator function is called not_one_of, but I imported it as excluded for easier comprehension) the input can’t have the values specified. In the form I created the password field can’t have the values ‘password’ or ‘qwerty’&lt;/li&gt;
&lt;li&gt;passwords_mismatch:(the validator function is called confirmed) the input must be the same value as the confirmation field. The confirmation field is passed in after the ‘@’ sign.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, on an incorrect entry, an error message would appear under the input field saying ‘field is not valid’. The user does not know the rules that an input field’s value is validating against, so it can become unclear as to why the value is wrong and why the system is rejecting it. This can quickly become annoying.&lt;/p&gt;

&lt;p&gt;To avoid bad user experience I decided to display each error separately. The number of errors that would appear depends on how many validation rules the input breaks. This is not the default behaviour for vee-validate because &lt;strong&gt;vee-validate opts for a fast exit strategy&lt;/strong&gt; for better performance. This means that when vee-validate comes across the first rule that is not met, it does not bother checking the rest of the rules, instead it exits the validation process.&lt;/p&gt;

&lt;p&gt;For this registration form I wanted to display all the error messages for the password field, so I made the following changes to the password field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Password --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block mb-2 text-white"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;vee-field&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;:bails=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"{ field, errors }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
&lt;span class="err"&gt;            &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
&lt;span class="err"&gt;            &lt;/span&gt;&lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Create password"&lt;/span&gt;
&lt;span class="err"&gt;            &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"&lt;/span&gt;
&lt;span class="err"&gt;            &lt;/span&gt;&lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-[#CF6679]"&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"error in errors"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            {{ error }}
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/vee-field&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Don't forget to remove the ErrorMessage component that was added to the password field previously.&lt;/p&gt;

&lt;p&gt;The first thing I did was to set the &lt;strong&gt;bails attribute&lt;/strong&gt; on the vee-field to false. This lets vee-validate know that it has to check all rules even if it encounters one that already broke a validation rule. Basically, it cancels the fast exit strategy.&lt;/p&gt;

&lt;p&gt;To render multiple error messages I had to tweak the behaviour of the vee-field component. The vee-field component makes use of &lt;strong&gt;scoped slots (v-slot)&lt;/strong&gt; feature which allows rendering a complex group of markup. I created an opening and closing tag for the vee-field because I wanted to add more markup inside it, such as the input tag and the error messages. I moved the type, class, and placeholder attributes to the input tag. The default slot gave me access to the &lt;strong&gt;‘field’ object&lt;/strong&gt; and an &lt;strong&gt;‘errors’ array&lt;/strong&gt;. According to the VeeValidate docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most crucial part of rendering fields with v-slot is that you bind the field object to your input element/input, the field object contains all the common attributes and listeners required for the field to be validated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After I made these changes, I added a div tag, looped through the errors array, and displayed each message. (Docs for further explanation: &lt;a href="https://vee-validate.logaretm.com/v4/api/field/#rendering-complex-fields-with-scoped-slots" rel="noopener noreferrer"&gt;https://vee-validate.logaretm.com/v4/api/field/#rendering-complex-fields-with-scoped-slots&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;At this point, the error messages showed up separately. The next problem I had to solve was to convert the messages into descriptive information because right now it even looks like the application is broken.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ihlxb693ni91wimmlb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ihlxb693ni91wimmlb.png" alt="multiple generic error messages" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I fixed this by adding &lt;strong&gt;custom error messages&lt;/strong&gt; to the validation rules. Back in the validation.js, I imported the configure function from vee-validate, and for each rule I added a custom message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="na"&gt;generateMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is required.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is too short.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is too long.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; must be a valid email.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`You are not allowed to use this value for the field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="na"&gt;passwords_mismatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The passwords don't match.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;          &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The field &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is invalid.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;generateMessage function&lt;/strong&gt; comes from vee-validate and it has to return a string. To register the generateMessage function I used the configure function from vee-validate. I accessed the &lt;strong&gt;FieldContext&lt;/strong&gt; (above as ‘ctx’) interface to have the information on the field name, value, and rule name (Docs for further explanation: &lt;a href="https://vee-validate.logaretm.com/v4/guide/i18n#global-message-generator" rel="noopener noreferrer"&gt;https://vee-validate.logaretm.com/v4/guide/i18n#global-message-generator&lt;/a&gt; ). I then created an object called messages with the rule names and the custom error messages I wanted. I defined the message variable’s value using a quick ternary operator, and after I returned the correct string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjc9jb28woslksnq1dkir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjc9jb28woslksnq1dkir.png" alt="multiple descriptive error messages" width="800" height="487"&gt;&lt;/a&gt;&lt;br&gt;
At last, the password field displays all the broken rules with a descriptive message.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
