<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abul Asar's Blog]]></title><description><![CDATA[Abul Asar's Blog]]></description><link>https://abulasar.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 16:42:01 GMT</lastBuildDate><atom:link href="https://abulasar.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Set Up Multipass with VS Code on Mac: A Step-by-Step Guide]]></title><description><![CDATA[Motivation
Recently, I tried to learn some low-level system programming stuff. I am a Mac user, and I thought that everything that works on Linux should also work on Mac. After all, Mac is a Unix-based system 😊. I guess we all heard this. Oh boy! I ...]]></description><link>https://abulasar.com/how-to-set-up-multipass-with-vs-code-on-mac-a-step-by-step-guide</link><guid isPermaLink="true">https://abulasar.com/how-to-set-up-multipass-with-vs-code-on-mac-a-step-by-step-guide</guid><category><![CDATA[#Multipass]]></category><category><![CDATA[systemprogramming]]></category><category><![CDATA[c programming]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Fri, 23 Jan 2026 05:06:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769108273750/b57e088d-b9c3-4efb-8536-2d61fd8e3bee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-motivation">Motivation</h3>
<p>Recently, I tried to learn some low-level system programming stuff. I am a Mac user, and I thought that everything that works on Linux should also work on Mac. After all, Mac is a Unix-based system 😊. I guess we all heard this. Oh boy! I was mistaken. After attempting to run a few programs, I realized I was wrong.</p>
<p>Below are a few points that restrict you from implementing system programming stuff on Mac.</p>
<ul>
<li><p>Kernel Mismatch: macOS ≠ Linux</p>
</li>
<li><p>macOS doesn't have the <strong>procfs</strong> or <strong>sysfs</strong> filesystems used heavily for interacting with kernel objects.</p>
</li>
<li><p>System programming, it requires a <strong>GCC toolchain,</strong> which you’d need to install using cross-compilation tools, and it comes at a price.</p>
</li>
</ul>
<p>These points suggest that doing system programming on macOS is not easy. Most of the necessary tools are not available on the system. You can either use virtual machines or Docker, but these options come with some performance costs. After some research, I found a tool called Multipass.</p>
<h3 id="heading-what-is-multipass">What is Multipass?</h3>
<p>Multipass is a super simple way to run an <strong>actual Ubuntu Linux VM</strong> on your Mac, without the usual “VM setup headache”. You install Multipass, run one command, and you get a clean Ubuntu machine in seconds. It’s made by Canonical (the Ubuntu guys), so the experience feels very “Ubuntu native” instead of a random virtual machine tool.</p>
<p>Using Multipass, you keep your Mac as your main workstation (terminal, editor, browser, notes, etc.), but all the “Linux-only” work happens inside the Ubuntu VM. So you can install <code>gcc</code>, <code>make</code>, <code>gdb/lldb</code>, kernel headers, compile C programs, and build/load kernel modules (inside the VM). For learning system programming, this is almost the best of both worlds, i.e., <strong>Mac + Linux</strong>.</p>
<h3 id="heading-how-to-install-multipass">How to Install multipass?</h3>
<ul>
<li><p>Open a search engine of your choice and run a search for “multipass”. Open the first result and search that will open Ubuntu documentation in that page, search <strong>(using cmd + f)</strong> for <code>Install Multipass</code> Or go to this <a target="_blank" href="https://documentation.ubuntu.com/multipass/latest/how-to-guides/install-multipass/">url</a>.</p>
</li>
<li><p>Either you can download the installer or you can install using <code>brew</code>.</p>
</li>
<li><p>Installing through the installer is very straightforward, and to install using <code>brew</code>, you need to run <code>brew install multipass</code>. It will take a couple of minutes to install.</p>
</li>
<li><p>Now, to confirm if it’s been install you need to run <code>multipass</code> In the terminal, you will see a bunch of helper commands, something like below</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769027872752/0d8d84fa-78bd-43a6-9053-e45d1fa810ff.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Now, we know that it is a lightweight virtual manager, which is currently empty, and we need to install an instance.</p>
</li>
<li><p>To do so, we need to run the command in the terminal <code>multipass shell</code> . It will take some time to create an instance named <code>primary</code> . It will install a Ubuntu virtual machine.</p>
</li>
<li><p>After some time, when it is done, the prompt will end, and then we need to enter the shell by running <code>multipass shell primary</code> . We will then be inside the Ubuntu virtual machine, and we will be able to see the screen like below</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769029888990/d6c78c15-a725-47be-901f-05668089220a.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h3 id="heading-how-to-connect-multipass-with-visual-studio-code">How to connect Multipass with Visual Studio Code?</h3>
<p>Now that our multishell is ready, you can see it is just an terminal based ubuntu system, and we don’t have gui to interact with. We can’t install VS Code inside it to access the environment. So, to harness the power of the Ubuntu environment, we need to connect VS Code on our Mac system to the Ubuntu system. We need to follow the steps below.</p>
<ul>
<li><p>Inside the Ubuntu terminal, run the following command: <code>sudo vim /etc/ssh/sshd_config</code> And we will make some changes in the config file to make password-based login into the system.</p>
</li>
<li><p>Inside the file, find “KbdInteractiveAuthentication no” and change it to <code>yes</code> something like this <code>KbdInteractiveAuthentication yes</code> .</p>
</li>
<li><p>Now, since we made the changes in the system, we will reload the daemon to see the effect of the changes made. To do so, run the command <code>sudo systemctl daemon-reload</code> .</p>
</li>
<li><p>Next, we will run <code>sudo service ssh restart</code> so that a remote ssh connection can be made.</p>
</li>
<li><p>Now, we will reset the password of the instance i.e <code>ubuntu</code> .(<strong>Note:</strong> <code>ubuntu@primary</code> What we can see in the prompt here <code>ubuntu</code> is the system name and <code>primary</code> the user name). Also, remember this password as it will be used to connect our VS Code with this Ubuntu instance.</p>
</li>
<li><p>We will run the command <code>sudo passwd ubuntu</code> . It will ask to enter and re-enter the password.</p>
</li>
<li><p>Now, open the Vs code and move to the Extensions tab and search for “remote development” and install the first result.</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769031209361/421c6c57-6cbb-4a97-a1b3-4edb0f7764fc.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>After installing this extension, you’ll be able to see a computer-like icon below the <code>Extension</code> button in VS Code. It is nothing but the “Remote Development” extension. Click on that, and it will open a panel with a list of <code>SSH</code> But it will be empty since we have just installed the extension.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769031448430/6c0fe00c-992a-4974-91cc-508924e0666e.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Click on the <strong>“+”</strong> button in the SSH section, and it will open an input box where we will enter <code>ubuntu@192.168.2.2</code> (<strong>192.168.2.2</strong> is the ip address of our Ubuntu instance, which we can get by running the command <code>hostname -I</code> in the <code>ubuntu@primary</code> terminal).</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769031883587/36c8885f-071b-42be-942b-e3bd15d9e7bb.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Press Enter <strong>twice</strong>, and now you will be able to see an entry under <code>SSH</code> section with the name of ip address, i.e., <strong>192.168.2.2.</strong></p>
</li>
<li><p>Now, click on the entry inside the SSH list, and you’ll see a prompt to enter the password. Enter the same password you added for the Ubuntu system.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769032150170/74f2356c-6cc5-412f-9078-5fa153a55fa8.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Now, this will connect our VS Code with the Ubuntu instance. To confirm this, create a directory inside the Ubuntu machine, say <code>test</code> , and inside the vscode click the “Open Folder” button, and you will see the <code>test</code> directory listed in the folders list. This confirms that our VS Code and Ubuntu instance are connected.</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769032396543/3dd03eea-03b6-400f-bf88-04f5701146ac.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Next, we will run some sample C code.</p>
</li>
</ul>
<h3 id="heading-writing-and-running-a-sample-c-program-in-a-multipass-environment">Writing and running a sample C program in a multipass environment?</h3>
<p>Now we know how to connect our VS Code with the Multipass environment. We will now run a sample C program to complete the motive of this blog. For this, we need to install <code>gcc</code> to run C/C++ programs. We can install <code>gcc</code> by running <code>sudo apt install build-essential</code> in ubuntu shell. This will install the <code>gcc</code> program. We can confirm if its install by running <code>gcc —version</code>.<br />Finally, we can create a sample <code>hello_world.c</code> C file in the <code>test</code> folder we created earlier in the Ubuntu instance by running <code>touch hello_world.c</code> or simply in the VS Code. You can see the file in the SSH section of VS Code, something like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769107379757/68751743-b7e8-483e-b213-42f03e280275.png" alt class="image--center mx-auto" /></p>
<p>Add the following small code snippet to it</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Hello, World\n"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>And finally, we can compile the program through the Ubuntu terminal by running <code>gcc hello_world.c</code> and then by running <code>./a.out</code>It will print the result in the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769107523773/c65bf99d-5180-4a17-9b6b-574130e0ce49.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Multipass is the easiest way to get a clean Ubuntu Linux environment on macOS without changing your main setup. It keeps your Mac workflow intact while giving you real Linux tools and behavior, which is perfect for system programming.</p>
<p>I hope you like this blog on installing and setting up Multipass. If you have any questions, please comment below. Thanks for reading 😊.</p>
<p><strong>NOTE</strong>: If you wish to learn C/C++ and System Programming in great detail. You can connect and take training from <a target="_blank" href="https://www.linkedin.com/in/bhavithc/">Bhavith Achar</a>, who is the founder and creator of <a target="_blank" href="https://www.e-grasp.com/">e-grasp.com</a>. He is an excellent C/C++ Engineer and a teacher, and I have taken System Programming training from him.</p>
]]></content:encoded></item><item><title><![CDATA[TIL: Debugging "Column Cannot Be Null" Error in MySQL despite nullable column definition]]></title><description><![CDATA[Problem Statement
While working on a Nestjs project, I encountered a weird problem related to the database column. I was trying to insert a record into a MySQL table using TypeORM. The error I was experiencing stated that a specific column “cannot be...]]></description><link>https://abulasar.com/til-debugging-column-cannot-be-null-error-in-mysql-despite-nullable-column-definition</link><guid isPermaLink="true">https://abulasar.com/til-debugging-column-cannot-be-null-error-in-mysql-despite-nullable-column-definition</guid><category><![CDATA[Databases]]></category><category><![CDATA[MySQL]]></category><category><![CDATA[Null Safety]]></category><category><![CDATA[Database triggers]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Tue, 29 Apr 2025 13:12:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745931666717/dd264a0f-96c2-4784-96b4-72be53d546c8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-problem-statement"><strong>Problem Statement</strong></h3>
<p>While working on a <code>Nestjs</code> project, I encountered a weird problem related to the database column. I was trying to insert a record into a MySQL table using TypeORM. The error I was experiencing stated that a specific column “<strong>cannot be null”</strong>, even though the column was explicitly defined as <strong>nullable</strong>.</p>
<h3 id="heading-investigation-amp-root-cause"><strong>Investigation &amp; Root Cause</strong></h3>
<p>I performed the following checks to identify the root cause:</p>
<ol>
<li><h4 id="heading-checked-orm-entity-amp-column-definition"><strong>Checked ORM Entity &amp; Column Definition:</strong></h4>
<pre><code class="lang-typescript"> <span class="hljs-meta">@Column</span>({ <span class="hljs-keyword">type</span>: <span class="hljs-string">'int'</span>, nullable: <span class="hljs-literal">true</span> })
 columnName?: <span class="hljs-built_in">number</span> | <span class="hljs-literal">null</span>;
</code></pre>
<p> The column was correctly marked as <strong>nullable</strong> in the ORM model.</p>
</li>
<li><p><strong>Verified Database Schema using</strong> <code>SHOW CREATE TABLE</code></p>
<pre><code class="lang-sql"> <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-string">`table_name`</span> (
   <span class="hljs-string">`columnName`</span> <span class="hljs-built_in">int</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-literal">NULL</span>,
   ...
 );
</code></pre>
<p> To make sure everything is good at the table structure level, I ran this command <code>SHOW CREATE TABLE TABLE_NAME</code>. This command gives the details of the table and how it is structured. The definition explicitly stated that the column could be null.</p>
</li>
<li><p><strong>Checked for Foreign Key Constraints using</strong> <code>SHOW CREATE TABLE</code> &amp; <code>SHOW TABLE STATUS</code></p>
<p> No explicit foreign key constraint was found enforcing a non-null value.</p>
</li>
<li><p><strong>Checked Active Triggers using</strong> <code>SHOW TRIGGERS WHERE Table = 'table_name';</code></p>
<p> Discovered <strong>one or more triggers</strong> (e.g., <code>table_name_insert</code>, <code>table_name_update</code>) that were modifying data before the insert/update operation.</p>
</li>
<li><p><strong>Issue Identified:</strong></p>
<ul>
<li>A <strong>trigger</strong> was enforcing a non-null constraint on the column, even though the schema allowed NULL values.</li>
</ul>
</li>
</ol>
<h3 id="heading-solution"><strong>Solution</strong></h3>
<p>Option 1: Remove the Trigger (if unnecessary)</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TRIGGER</span> trigger_name;
</code></pre>
<p>The easiest option to fix this problem was to remove the trigger, which is causing the problem.</p>
<p><strong>Option 2: Modify the Trigger to Handle Null Values Properly</strong></p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TRIGGER</span> trigger_name
<span class="hljs-keyword">BEFORE</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">ON</span> table_name
<span class="hljs-keyword">FOR</span> <span class="hljs-keyword">EACH</span> <span class="hljs-keyword">ROW</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">IF</span> NEW.columnName <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">THEN</span>
    <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> log_table (columnName, <span class="hljs-keyword">action</span>)
    <span class="hljs-keyword">VALUES</span> (NEW.columnName, <span class="hljs-string">'INSERT'</span>);
  <span class="hljs-keyword">END</span> <span class="hljs-keyword">IF</span>;
<span class="hljs-keyword">END</span>;
</code></pre>
<p>We can add a safety check to prevent errors when the inserted record is NULL.</p>
<p><strong>Option 3: Allow NULL values in the log table</strong></p>
<p>We can modify the column definition in the target <code>log_table</code> to allow NULLs.</p>
<h3 id="heading-conclusion"><strong>Conclusion:</strong></h3>
<ul>
<li><p>Even if a column is <strong>nullable</strong>, <strong>triggers</strong> can override this behavior.</p>
</li>
<li><p>Always check active triggers using</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">TRIGGERS</span> <span class="hljs-keyword">WHERE</span> <span class="hljs-string">`Table`</span> = <span class="hljs-string">'table_name'</span>;
</code></pre>
</li>
<li><p>Modifying or disabling triggers can resolve unexpected <code>Column Cannot Be Null</code> errors.</p>
</li>
</ul>
<p>I hope you like this small TIL blog. If you have any questions, please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Understand and implement Builder Design Pattern in Javascript and C++]]></title><description><![CDATA[Recently, I was exploring design patterns courses on my LinkedIn Learning subscription. I came across a course, Node.js: Design Patterns by Alex Banks. It is a wonderful, easy-to-understand course. I started with the Builder Pattern, and the explanat...]]></description><link>https://abulasar.com/understand-and-implement-builder-design-pattern-in-javascript-and-c</link><guid isPermaLink="true">https://abulasar.com/understand-and-implement-builder-design-pattern-in-javascript-and-c</guid><category><![CDATA[design patterns]]></category><category><![CDATA[C++]]></category><category><![CDATA[builder pattern]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Wed, 20 Nov 2024 14:49:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/CSjPUOG7210/upload/0ab64e24a5414640321bae5d61a0cdc2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I was exploring design patterns courses on my LinkedIn Learning subscription. I came across a course, <code>Node.js: Design Patterns</code> by <code>Alex Banks</code>. It is a wonderful, easy-to-understand course. I started with the Builder Pattern, and the explanation was excellent, so I thought, why not try implementing it in C++? In this blog, I will explain what the “<strong>Builder Pattern</strong>” is and show its implementation in <strong>JavaScript</strong>, similar to Alex’s example, to understand it better. Later, I will implement it in <strong>C++</strong>.</p>
<h3 id="heading-what-is-a-builder-pattern">What is a Builder Pattern?</h3>
<p>The <strong>Builder Pattern</strong> is a <strong>creational</strong> design pattern that provides a flexible solution for constructing complex objects. In other words, it simplifies object construction. It allows for the step-by-step creation of objects, enabling the customization of the object construction process. For example, when constructing a Person object, instead of providing long parameters to the constructor (as a configuration option), we can allow clients to configure the Person objects one piece at a time.</p>
<p>Imagine you are passing a list of parameters like booleans and strings such as <code>isManager</code>, <code>isEmployee</code>, <code>hours</code>, and <code>name</code> to construct a user object. You would pass these parameters like this: <code>Person(true, true, 40, "Julie")</code>. First of all, this looks very messy, and you need to know the order in which these parameters are expected in the Person's constructor. We can improve this with the Builder Pattern.</p>
<h3 id="heading-why-use-builder-pattern">Why use Builder Pattern?</h3>
<p>Two of the most common doubts I see on the internet, and I also had this in the beginning, are: “<strong>Can’t we directly pass an object and, based on that, create an object and add everything within a single Person class instead of using the Builder Pattern?</strong>”. Another question is, "<strong>Can’t</strong> <strong>we use a Typescript interface to enforce the object creation according to the defined interface, something like this?”</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> PersonModal {
  name: <span class="hljs-built_in">String</span>;
  isEmployee: <span class="hljs-built_in">boolean</span>;
  isManager?:<span class="hljs-built_in">Boolean</span>;
  hours?: <span class="hljs-built_in">Number</span>;
}
</code></pre>
<p>The answer to both questions is <strong>yes</strong>, you can do it, but the <strong>Builder Pattern</strong> makes the code more readable and easy to use. Its usage is like a <strong>Lego</strong> box where you have different parts, and you can construct an object as per your need.</p>
<p>You give the responsibility for object creation to a separate builder class instead of its definition class. You just have to add dot notation and method name, and you can add a feature to your object creation. Its main usage is the separation of the construction of complex objects from their representation (official definition). Imagine you are dumping all complex logic in the same representation class, i.e., the <code>Person</code> class. It will eventually become tough to manage. Refer to the pseudo-code to understand what I mean by plug-and-play and how can we construct and object.</p>
<pre><code class="lang-markdown">Person -- make --&gt; Manager() -- make --&gt; partTimeEmployee()
</code></pre>
<p>In code, it looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">new</span> PersonBuilder()
      .makeEmployee()
      .makeManager()
      .build(<span class="hljs-string">"Julie"</span>);
</code></pre>
<h3 id="heading-when-not-to-use-builder-pattern">When not to use Builder Pattern?</h3>
<p>Till now we’ve seen all good things about Builder Pattern and how it can be help in constructing complex objects. There are few scenario’s where we cannot use (or avoid it). Below are the few points:</p>
<ul>
<li><p>When the object is simple, with few attributes.</p>
</li>
<li><p>When performance is critical because this pattern introduce overhead because of multiple function calls for each attribute assignment.</p>
</li>
<li><p>When object structure is stable or unlikely to change builder pattern can be overkill.</p>
</li>
</ul>
<h3 id="heading-code-examples-in-javascript-and-c">Code Examples in Javascript and C++</h3>
<h4 id="heading-javascript-version">Javascript version</h4>
<pre><code class="lang-javascript"><span class="hljs-comment">// Person.js</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> </span>{
  <span class="hljs-keyword">constructor</span>(builder) {
    <span class="hljs-built_in">this</span>.name = builder.name;  
    <span class="hljs-built_in">this</span>.isEmployee = builder.isEmployee; 
    <span class="hljs-built_in">this</span>.isManager = builder.isManager; 
    <span class="hljs-built_in">this</span>.hours = builder.hours || <span class="hljs-number">0</span>;
  }

  toString() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>);
  }
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Person;

<span class="hljs-comment">// PersonBuilder.js</span>
<span class="hljs-keyword">import</span> Person <span class="hljs-keyword">from</span> <span class="hljs-string">"./Person.js"</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonBuilder</span> </span>{
  makeEmployee() {
    <span class="hljs-built_in">this</span>.isEmployee = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  makeManager() {
    <span class="hljs-built_in">this</span>.isManager = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  addShoppingList(list) {
    <span class="hljs-built_in">this</span>.shoppingList = list;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  build(name) {
    <span class="hljs-built_in">this</span>.name = name;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(<span class="hljs-built_in">this</span>);
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PersonBuilder;

<span class="hljs-comment">// index.js</span>
<span class="hljs-keyword">import</span> PersonBuilder <span class="hljs-keyword">from</span> <span class="hljs-string">"./PersonBuilder.js"</span>;

<span class="hljs-keyword">const</span> Julie = <span class="hljs-keyword">new</span> PersonBuilder()
                      .makeEmployee()
                      .makeManager()
                      .build(<span class="hljs-string">"Julie"</span>);

<span class="hljs-built_in">console</span>.log(Julie);
</code></pre>
<h4 id="heading-c-version">C++ version</h4>
<pre><code class="lang-cpp"><span class="hljs-comment">// person.h</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonBuilder</span>;</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> {</span>
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> name;  
    <span class="hljs-keyword">bool</span> isEmployee = <span class="hljs-literal">false</span>; 
    <span class="hljs-keyword">bool</span> isManager = <span class="hljs-literal">false</span>; 
    <span class="hljs-keyword">int</span> hours = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">friend</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonBuilder</span>;</span>

    <span class="hljs-keyword">public</span>:
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">printUserDetails</span><span class="hljs-params">()</span></span>;
};

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonBuilder</span> {</span>
    Person person;

    <span class="hljs-keyword">public</span>:
    <span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">setName</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&amp; name)</span></span>;
    <span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">makeEmployee</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">makeManager</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">addWorkingHours</span><span class="hljs-params">(<span class="hljs-keyword">int</span> hours)</span></span>;
    <span class="hljs-function">Person <span class="hljs-title">build</span><span class="hljs-params">()</span></span>;
};


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

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"person.h"</span></span>

<span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">PersonBuilder::makeEmployee</span><span class="hljs-params">()</span> </span>{
    person.isEmployee = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;
}

<span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">PersonBuilder::makeManager</span><span class="hljs-params">()</span> </span>{
    person.isManager = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;
}

<span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">PersonBuilder::addWorkingHours</span><span class="hljs-params">(<span class="hljs-keyword">int</span> workingHours)</span> </span>{
    person.hours = workingHours;
    <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;
}

<span class="hljs-function">PersonBuilder&amp; <span class="hljs-title">PersonBuilder::setName</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&amp; name)</span> </span>{
    person.name = name;
    <span class="hljs-keyword">return</span> *<span class="hljs-keyword">this</span>;
}

<span class="hljs-function">Person <span class="hljs-title">PersonBuilder::build</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> person;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Person::printUserDetails</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Name: "</span> &lt;&lt; name 
              &lt;&lt; <span class="hljs-string">", isEmployee: "</span> &lt;&lt; isEmployee 
              &lt;&lt; <span class="hljs-string">", isManager: "</span> &lt;&lt; isManager 
              &lt;&lt; <span class="hljs-string">", Working Hours: "</span> &lt;&lt; hours &lt;&lt; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    Person julie = PersonBuilder()
                        .setName(<span class="hljs-string">"Julie"</span>)
                        .makeEmployee()
                        .build();
    julie.printUserDetails();
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>The Builder Pattern is easy to understand and a good starting point for learning OOP design patterns. I’ll be writing about other design patterns in upcoming blog posts. I hope you like this blog. If you have any questions, please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Implement Table DOM Search in Phoenix LiveView]]></title><description><![CDATA[Suppose you are working on a table in a LiveView project. This table has limited static data of not more than one page (you can avoid questions about pagination in the comment section 😊). From a user's point of view, it becomes hard to look into the...]]></description><link>https://abulasar.com/implement-table-dom-search-in-phoenix-liveview</link><guid isPermaLink="true">https://abulasar.com/implement-table-dom-search-in-phoenix-liveview</guid><category><![CDATA[phoenix liveview]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[DOM]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[HTML]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Mon, 09 Sep 2024 06:17:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746039442421/66aa8193-8022-4ad5-b116-231a24ea4b2b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Suppose you are working on a table in a LiveView project. This table has limited static data of not more than one page (you can avoid questions about pagination in the comment section 😊). From a user's point of view, it becomes hard to look into the table for a specific record. Of course, a simple solution could be using a browser search by pressing <strong>Cmd + F</strong> and then searching the text, but not everyone is a tech expert who knows these kinds of hacks. In this blog, we will create a simple search feature on the table highlighting the most matched row (very similar to the <strong>Cmd + F</strong> search we discussed above). Refer to the gif image below to get the idea.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714841811479/335d8128-f615-410b-b393-fe674e5ea618.gif" alt class="image--center mx-auto" /></p>
<h3 id="heading-getting-started"><strong>Getting started</strong></h3>
<p>First of all, we should have a LiveView project. On any page of the project, add a table with a few dummy records or render real records from the database in the table. The records should be enough to make the table scrollable to transition to the particular searched record. Now let's get started.</p>
<h3 id="heading-add-search-input"><strong>Add search input</strong></h3>
<p>As per the demo gif you saw earlier, adding an input field is the main ingredient to achieve the above feature. So, we are going to add a search input above the table. Make sure to add <code>input</code> , and the <code>table</code> code in the same container <code>div</code>. This is because the search input and the table will be used in the javascript hook for DOM access. Refer to the code below.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"user-table-container"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"user-table-container"</span> <span class="hljs-attr">phx-hook</span>=<span class="hljs-string">"SearchUser"</span>&gt;</span>
  //Add below container to add search input
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"search-container"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"search"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-sm"</span>&gt;</span>Search<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"search"</span>  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">" Search..."</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"search-user"</span>&gt;</span>    
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span> 
     <span class="hljs-tag">&lt;<span class="hljs-name">thead</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-50"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-3 text-left text-sm"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-3 text-left text-sm"</span>&gt;</span>&gt;Name<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"col"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-3 text-left text-sm"</span>&gt;</span>&gt;Count<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
       <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"divide-y divide-gray-200 bg-white"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">%=</span> <span class="hljs-attr">for</span> {<span class="hljs-attr">record</span>, <span class="hljs-attr">index</span>} &lt;<span class="hljs-attr">-</span> <span class="hljs-attr">Enum.with_index</span>(@<span class="hljs-attr">records</span>) <span class="hljs-attr">do</span> %&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"whitespace-nowrap px-3 py-4 text-sm text-gray-500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"whitespace-nowrap px-3 py-4 text-sm text-gray-500 user-name"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">%=</span> <span class="hljs-attr">record.name</span> %&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"whitespace-nowrap px-3 py-4 text-sm text-gray-500"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">%=</span> <span class="hljs-attr">record.count</span> %&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">%</span> <span class="hljs-attr">end</span> %&gt;</span>  
     <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span> 
  <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-adding-javascript-hook"><strong>Adding Javascript Hook</strong></h3>
<p>As per the title of this blog, it is quite obvious that the solution revolves around DOM (Document Object Manipulation). DOM manipulation is achieved using JavaScript. To integrate JavaScript into our LiveView app, LiveView has a concept of JS Hooks. JS Hooks allows us to register JavaScript lifecycle callbacks on DOM nodes (To know more about Hooks, I wrote a <a target="_blank" href="https://abulasar.com/adding-infinite-scroll-in-phoenix-liveview-app">blog</a> about Infinite Scroll in LiveView and explained Hooks in great detail). We will add a file <code>assets/js/user_search.js</code> with the following code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SearchUser = {
    mounted() {
        <span class="hljs-keyword">const</span> searchInput = <span class="hljs-built_in">this</span>.el.querySelector(<span class="hljs-string">"input"</span>)
        <span class="hljs-built_in">console</span>.log(searchInput)
    }
}
</code></pre>
<p>Notice, the <code>mounted</code> function, it is the first code that executed when the Hook is initialized( Please read this <a target="_blank" href="https://abulasar.com/adding-infinite-scroll-in-phoenix-liveview-app">blog</a> if this doesn't make sense). The above code finds the input field on the web page (where users type their search queries) using <code>querySelector</code>. This will be the starting point of the JavaScript logic. Now, whenever a user starts to type in the search bar, the hook will detect the change and log the <code>searchInput</code> object in the console. In the next section, we will look at the remaining work and explanation of the logic of the DOM search.</p>
<h3 id="heading-search-code-explanation">Search code explanation</h3>
<p>Refer to the complete code of the hook. You've already seen the explanation of <code>searchInput</code>. Now, let's look into how each line of the code works.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SearchUser = {
  mounted() {
    <span class="hljs-keyword">const</span> searchInput = <span class="hljs-built_in">this</span>.el.querySelector(<span class="hljs-string">"input"</span>)

    searchInput.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">let</span> searchQuery = <span class="hljs-built_in">this</span>.el.querySelector(<span class="hljs-string">"input"</span>).value

        <span class="hljs-keyword">const</span> tableRows = <span class="hljs-built_in">this</span>.el.querySelectorAll(<span class="hljs-string">'table tbody tr'</span>)

        tableRows.forEach(<span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> {
          <span class="hljs-comment">// Get the text content of the row</span>
          <span class="hljs-keyword">const</span> rowData = row.textContent.toLowerCase()
          <span class="hljs-keyword">const</span> userNameContainer = row.querySelector(<span class="hljs-string">'.user-name'</span>)
          <span class="hljs-keyword">if</span> (searchQuery.length &amp;&amp; rowData.includes(searchQuery.toLowerCase())) {
            <span class="hljs-comment">// Move focus to the row</span>
            userNameContainer.classList.add(<span class="hljs-string">'ring-4'</span>) <span class="hljs-comment">// `ring-4` is a tailwind class.</span>
            row.scrollIntoView({<span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>, <span class="hljs-attr">block</span>: <span class="hljs-string">"center"</span>, <span class="hljs-attr">inline</span>: <span class="hljs-string">"nearest"</span>})
          } <span class="hljs-keyword">else</span> {
            userNameContainer.classList.remove(<span class="hljs-string">'ring-4'</span>) <span class="hljs-comment">// `ring-4` is a tailwind class.</span>
          }

        });

        <span class="hljs-keyword">if</span> (rowData.includes(searchQuery)) {
          row.scrollIntoView({<span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>, <span class="hljs-attr">block</span>: <span class="hljs-string">"center"</span>, <span class="hljs-attr">inline</span>: <span class="hljs-string">"nearest"</span>})
        }
      }
    );  
  }
}
</code></pre>
<h4 id="heading-listening-to-search-events">Listening to search events:</h4>
<p>We want to listen for the user's typing event. As soon as the user starts typing in the search input, we need to begin searching for the element. To do this, we add an event listener that triggers whenever the user types into the input field (using the <code>input</code> event). The <code>addEventListener</code> method handles this for us.</p>
<pre><code class="lang-javascript">searchInput.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> searchQuery = <span class="hljs-built_in">this</span>.el.querySelector(<span class="hljs-string">"input"</span>).value
}
</code></pre>
<p>We will also get the searched query into a variable <code>searchQuery</code>.</p>
<h4 id="heading-getting-the-rows">Getting the rows:</h4>
<p>Whatever search text we get in the event listener, we need to use that text to filter the table. For that, we will first need to get all the records on the page. We want to select all the table rows (<code>&lt;tr&gt;</code>) inside the body (<code>&lt;tbody&gt;</code>) of a table (<code>&lt;table&gt;</code>). We will do this using <code>querySelectorAll</code>, which returns a list (or a NodeList) of all matching rows in the table.</p>
<p><code>const tableRows = this.el.querySelectorAll('table tbody tr')</code></p>
<ul>
<li><p><code>this.el</code> refers to the parent element that contains the table. It's used to scope the search within that element.</p>
</li>
<li><p><code>'table tbody tr'</code> is the CSS selector used to find all the <code>&lt;tr&gt;</code> elements (table rows) that are inside <code>&lt;tbody&gt;</code> of a <code>&lt;table&gt;</code>.</p>
</li>
</ul>
<p>This line essentially collects all the rows in the table so that we can later loop through each of them to check their content.</p>
<h4 id="heading-get-each-row-content">Get each row content:</h4>
<p>Now, we are going to loop over each row of the table we queried in the previous step. For each row in the table, it converts the row's text to lowercase (to make the search case insensitive) and checks if the row contains the search query.</p>
<pre><code class="lang-javascript">tableRows.forEach(<span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> {
  <span class="hljs-comment">// Get the text content of the row</span>
  <span class="hljs-keyword">const</span> rowData = row.textContent.toLowerCase()
  ...
  ...
})
</code></pre>
<h4 id="heading-highlight-matching-rows">Highlight Matching Rows:</h4>
<p>We are going to search for the <code>name</code> of the user in the table. If you remember (or you can go through the <code>html</code> code), we have added the <code>user-name</code> class to the <code>name</code> <code>td</code> in each row. We will query the <code>user-name</code> element and save it in <code>userNameContainer</code>. Later, we will use it to highlight (add a border) or de-highlight (remove border) matched rows.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> userNameContainer = row.querySelector(<span class="hljs-string">'.user-name'</span>)
</code></pre>
<p>We will add a condition to check if <code>searchQuery</code> has any content and if each row contains the <code>searchQuery</code>. If the row contains the search term, the row is highlighted by adding a <code>ring-4</code> class (this is a Tailwind CSS class that adds a visible border around the row) and also scrolls smoothly to the row, making it visible using <code>scrollIntoView</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (searchQuery.length &amp;&amp; rowData.includes(searchQuery.toLowerCase())) {
  <span class="hljs-comment">// Move focus to the row</span>
  userNameContainer.classList.add(<span class="hljs-string">'ring-4'</span>) 
  row.scrollIntoView({<span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>, <span class="hljs-attr">block</span>: <span class="hljs-string">"center"</span>, <span class="hljs-attr">inline</span>: <span class="hljs-string">"nearest"</span>})
} <span class="hljs-keyword">else</span> {
  userNameContainer.classList.remove(<span class="hljs-string">'ring-4'</span>) <span class="hljs-comment">// `ring-4` is a tailwind class.</span>
}
</code></pre>
<p>Similarly, when if the <code>searchQuery</code> doesn't match remove the previous highlighted class i.e <code>ring-4</code></p>
<h4 id="heading-scroll-to-matched-rows">Scroll to Matched Rows:</h4>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (rowData.includes(searchQuery)) {
  row.scrollIntoView({<span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>, <span class="hljs-attr">block</span>: <span class="hljs-string">"center"</span>, <span class="hljs-attr">inline</span>: <span class="hljs-string">"nearest"</span>})
}
</code></pre>
<p>This code checks if a specific table row contains the user's searched query. If it does, the page scrolls smoothly to bring the matching row into view, centered on the screen.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you like this blog. If you have any questions please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Creating Tabular Representation of Database Indexes using Elixir Mix Task]]></title><description><![CDATA[Recently, while working on one of my personal Elixir projects. I came across a scenario where I needed to make some changes to the database schema.The changes mainly revolve around adding and removing indexes due to changes in the business requiremen...]]></description><link>https://abulasar.com/creating-tabular-representation-of-database-indexes-using-elixir-mix-task</link><guid isPermaLink="true">https://abulasar.com/creating-tabular-representation-of-database-indexes-using-elixir-mix-task</guid><category><![CDATA[Elixir]]></category><category><![CDATA[tasks]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Wed, 05 Jun 2024 20:38:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/eygpU6KfOBk/upload/787281660399c8436264d55cd954649b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, while working on one of my personal Elixir projects. I came across a scenario where I needed to make some changes to the database schema.The changes mainly revolve around adding and removing <code>indexes</code> due to changes in the business requirements. Every time I made the changes by running the migrations I was going through the hassle of checking the columns in the database. To see the list of indexes in each table I had to run</p>
<ul>
<li><p><code>\d table_name</code> in the Postgres terminal or</p>
</li>
<li><p>Run the query <code>SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'your_table_name';</code> in the database admin tools like <code>Beaver</code> or <code>PgAdmin</code>.</p>
</li>
</ul>
<p>Since the changes were about indexes, I had to be extra cautious to understand their details. So, I was wondering if there could be a way I could see the result soon after running the migrations or whenever I need to view the list of indexes in the same terminal I am working. After doing some tinkering, a <code>mix task</code> was something that came to my mind to achieve the goal. Let's understand the requirements more deeply to make development easier.</p>
<h3 id="heading-requirement">Requirement</h3>
<p>We want a <code>mix</code> command that will list all the <code>indexes</code> in our application database. We will run the command <code>mix list_indexes</code> and it will render all the indexes in our application. Refer to the image below to get an idea of what output the command is going to render.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715611820971/ca0b5bfe-0d88-49a9-bed4-da2190f8856a.png" alt class="image--center mx-auto" /></p>
<p>Notice, the perfect tabular structure its borders, gaps, etc. We're going to develop the logic to replicate this exactly. So, let's get started.</p>
<h3 id="heading-adding-mix-task-command">Adding mix task command</h3>
<p>Before starting to create a mix task (command) first I'll give you a brief introduction to what is a <code>mix task</code>. In any Phoenix project when we run <code>mix help</code> it lists all the custom mix tasks. Refer to the example diagram below to get an idea.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717619653328/23199b8f-dd8e-4e33-a5aa-2ae6f8b9e427.jpeg" alt class="image--center mx-auto" /></p>
<p>Now, we want to write our custom mix task to render the list of indexes. To achieve this, we will create a file <code>lib/mix/tasks/list_indexes.ex</code>. In the file, we will add the following code</p>
<pre><code class="lang-elixir">  <span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Mix.Tasks.ListIndexes</span></span> <span class="hljs-keyword">do</span>
      <span class="hljs-keyword">use</span> Mix.Task

      <span class="hljs-variable">@shortdoc</span> <span class="hljs-string">"To list all the indexes in the database"</span>

      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span></span>(_args) <span class="hljs-keyword">do</span>
           <span class="hljs-comment"># Write your task logic here</span>
      <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
</code></pre>
<p>If you'll notice we have added <code>use Mix.Task</code> macro, which adds task capability to this file. To list this task within our application we can run <code>mix help</code> in the project terminal. You will get the list of the <code>tasks</code> but won't find the <code>task</code> we created. It is because we need to compile the project. We will do this by running <code>mix compile</code>. Now on running <code>mix compile</code> and later running <code>mix help</code> again we can see the task we created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715667799617/da505b4a-3626-419a-81e5-e758aa4b754d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-writing-the-main-logic">Writing the main logic</h3>
<p>Up until now, we understood the requirements and what we wanted to achieve and we set up the code structure. The main logic is remaining, so this will be our approach</p>
<ul>
<li><p>Query database to fetch all the indexes using Ecto.</p>
</li>
<li><p>Get the longest name in each column</p>
</li>
<li><p>Render header</p>
</li>
<li><p>Render all rows</p>
</li>
</ul>
<h4 id="heading-query-database-to-fetch-all-the-indexes-using-ecto">Query database to fetch all the indexes using Ecto.</h4>
<p>This will be the simplest part of this blog. We will write a simple Ecto query to fetch all the indexes in the project, which will be passed down for further processing.</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Mix.Tasks.ListIndexes</span></span> <span class="hljs-keyword">do</span>
     ............
     ............
     ............
     query = from p <span class="hljs-keyword">in</span> <span class="hljs-string">"pg_indexes"</span>,
        <span class="hljs-symbol">select:</span> %{<span class="hljs-symbol">index_name:</span> p.indexname, <span class="hljs-symbol">table_name:</span> p.tablename},
        <span class="hljs-symbol">where:</span> p.schemaname == <span class="hljs-string">"public"</span>

     rows = Repo.all(query)

<span class="hljs-keyword">end</span>
</code></pre>
<p>The query is quite simple and intuitive. We are querying the <code>pg_indexes</code> table, which is part of the <code>public</code> schema. For the sake of the demo, we are just querying <code>index_name</code> and <code>table_name</code>. Later, we pass the query to <code>Repo.all</code> to fetch it. To make the <code>query</code> (syntax) and <code>Repo.all</code> work, we have aliased <code>Ecto.Query</code> and imported <code>Repo</code>. This will return a list of structs with <code>index_name</code> and <code>table_name</code>, something like this:</p>
<pre><code class="lang-elixir">[
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"schema_migrations_pkey"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"schema_migrations"</span>},
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"distributors_pkey"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"distributors"</span>},
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"distributors_email_index"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"distributors"</span>},
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"distributors_tokens_pkey"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"distributors_tokens"</span>},
  .....
]
</code></pre>
<h4 id="heading-get-the-longest-name-in-each-column">Get the longest name in each column</h4>
<p>In this section, we will write a logic to find the longest string length for each column from fetched records. This will help us to add padding to each column. Suppose, you have two records for column <code>index_name</code> say <code>username_index</code> and <code>distributors_email_index</code>. The function we are going to write will return the length of <code>distributors_email_index</code> because it is the longest one. We will have to write this logic such that it will return a list with the length of two columns (since there are two columns <code>Index Name</code> and <code>Table Name</code>. The code will consist of two functions <code>longest_columns_values/1</code> and <code>find_longest_length/1</code>. First, we will understand the longest_columns_values.</p>
<p>👉 <code>longest_columns_values/1</code>:</p>
<p>This function takes a list of records as its argument. Each record is a map with keys <code>:index_name</code> and <code>:table_name</code>. The purpose of this function is to transform the records into a list of lists containing only the values of <code>:index_name</code> and <code>:table_name</code> and then find the longest length for each column by passing to the find_longest_length/1 function.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">longest_columns_values</span></span>(records) <span class="hljs-keyword">do</span>
  Enum.map(records, <span class="hljs-keyword">fn</span> index -&gt;
    [index.index_name, index.table_name]
  <span class="hljs-keyword">end</span>)
  |&gt; find_longest_length
<span class="hljs-keyword">end</span>
</code></pre>
<p>👉 <code>find_longest_length/1</code>:</p>
<p>This private function (you can avoid making it private) takes a list of columns of each row and calculates the maximum length of the strings in each column.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">find_longest_length</span></span>(rows) <span class="hljs-keyword">do</span>
  Enum.reduce(rows, [], <span class="hljs-keyword">fn</span> row, acc -&gt;
    Enum.zip(row, acc ++ [0])
    |&gt; Enum.map(<span class="hljs-keyword">fn</span> {str, len} -&gt;
      String.length(str) |&gt; Kernel.max(len)
    <span class="hljs-keyword">end</span>)
  <span class="hljs-keyword">end</span>)
<span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p><code>Enum.reduce(rows, [], fn row, acc -&gt; ... end)</code>:</p>
<p>  This line uses <code>Enum.reduce</code> to iterate over each row in rows. The <code>acc</code> (accumulator) starts as an empty list and is used to store the maximum lengths of each column.</p>
</li>
<li><p><code>Enum.zip(row, acc ++ [0])</code>:</p>
<p>  For each row, this line zips the current row with the accumulator. If the accumulator is shorter than the row, it is padded with zeros.</p>
</li>
<li><p><code>Enum.map(fn {str, len} -&gt; String.length(str) |&gt; Kernel.max(len) end)</code>:</p>
<p>  This line maps over the zipped list of {str, len} tuples. It calculates the length of str and compares it with len (the current maximum length), keeping the larger of the two.</p>
</li>
</ul>
<p>The result of find_longest_length/1 is a list of integers representing the maximum length of the strings in each column. Refer to the example to understand its usage.</p>
<pre><code class="lang-elixir">rows = [
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"schema_migrations_pkey"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"schema_migrations"</span>},
  %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"username_key"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"some_long_table_name"</span>},
]

IO.inspect(longest_field_values(rows))
<span class="hljs-comment">#=&gt; [ 22, 20] - 22 is the length of `schema_migrations_pkey` </span>
<span class="hljs-comment">#=&gt; because it is the longest name in the first column i.e. `:index_name`,</span>
<span class="hljs-comment">#=&gt; and 20 is the length of `some_long_table_name` </span>
<span class="hljs-comment">#=&gt; because it is the longest name in the second column i.e. `:table_name`.</span>
</code></pre>
<h4 id="heading-adding-the-header">Adding the header</h4>
<p>Now that we have written the logic to get the list of the widest columns(using <code>longest_columns_values</code>), we will use this list to render the table header. For this, we will write a function <code>render_single_row</code> which we will use to render the header, and later we'll reuse it to render all the rows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717609691653/ea80576d-ac6c-4ce2-b0cf-f76333c06d01.png" alt class="image--center mx-auto" /></p>
<p>Notice the vertical line <code>|</code> at the start, end, and in the middle of the two columns. The start line will be easy to add, but it will be tougher to add the middle and end lines. We don't know how much space we need to leave after each column letter. This is where <code>longest_columns_values</code> will shine. But how are we going to use it? There is a formula that we are going to follow to give space after each column and the formula is</p>
<pre><code class="lang-plaintext">spaces_count = widest_column_for_the_position - column_name
</code></pre>
<p>\=&gt; Explanation</p>
<p>We have seen earlier in the previous section <code>widest_column_counts = [22, 20]</code> and as per the screenshot, the first column is <code>Index Name</code> which is of length <code>10</code> . The <code>Index Name</code> is the first column so we will use the first element of <code>widest_column_counts</code> . Hence, the value will be <code>space_count</code> will be <code>20-10=10</code> .</p>
<p>So, we will write a <code>render_single_row/2</code> function as per the above explanation. It will take two arguments i.e <code>row</code> (header in this case) and <code>widest_column_counts</code> .</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_single_row</span></span>(%{<span class="hljs-symbol">index_name:</span> index_name, <span class="hljs-symbol">table_name:</span> table} , widest_column_counts) <span class="hljs-keyword">do</span>
    IO.write(<span class="hljs-string">"| "</span>)

    [index_name, table]
      |&gt; Enum.with_index()
      |&gt; Enum.each(<span class="hljs-keyword">fn</span> {column_content, index} -&gt;
          border = render_border(index, column_content, widest_column_counts)
          IO.write(<span class="hljs-string">"<span class="hljs-subst">#{item}</span> <span class="hljs-subst">#{border}</span>"</span>)
        <span class="hljs-keyword">end</span>)

    IO.puts(<span class="hljs-string">" "</span>) <span class="hljs-comment">#Next line</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>As you can see the function starts with rendering the leftest vertical line <code>|</code> . Then we iterate over the list of columns and render the content using <code>IO.write("#{column_content} #{border}")</code>. So, the above logic is nothing but the entire row rendering logic.</p>
<p>Also, we will write a function <code>render_border</code> which will render ending vertical lines along with trailing spaces as discussed earlier. It will take three parameters <code>position_of_column</code> , <code>item - the item to be rendered</code> and <code>columns_count</code></p>
<pre><code class="lang-elixir">
<span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_border</span></span>(position_of_column, item, columns_count) <span class="hljs-keyword">do</span>
    count = Enum.at(columns_count, position_of_column) - String.length(item)
    <span class="hljs-string">"<span class="hljs-subst">#{String.duplicate(<span class="hljs-string">" "</span>, count)}</span> | "</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>The above-written code logic has already been explained. We are just fetching <code>space_counts</code> and we are duplicating spaces <code>" "</code> as per <code>count</code> value and ending with a vertical line <code>|</code> .</p>
<h4 id="heading-render-all-rows">Render all rows</h4>
<p>This is the final section of the table render logic. Remember we fetched the records, and it was nothing but a list of records. We've already completed the logic to render a single row. Now, we have to render a list of rows. For this, we just have to iterate over the <code>rows</code>. Below is the function that will do this job.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_multiple_rows</span></span>(rows, widest_column_counts) <span class="hljs-keyword">do</span>
  Enum.each(rows, <span class="hljs-keyword">fn</span> row -&gt; render_single_row(row, widest_column_counts) <span class="hljs-keyword">end</span>)
<span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-final-working-code">Final working code</h3>
<pre><code class="lang-elixir"> <span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">Mix.Tasks.ListIndexes</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> Mix.Task

  <span class="hljs-variable">@shortdoc</span> <span class="hljs-string">"To list all the indexes in the table"</span>

  <span class="hljs-keyword">alias</span> MyProject.{ Repo }
  <span class="hljs-keyword">import</span> Ecto.Query


  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span></span>(_args) <span class="hljs-keyword">do</span>
    Mix.Task.run(<span class="hljs-string">"app.start"</span>, [Repo])

    query = from p <span class="hljs-keyword">in</span> <span class="hljs-string">"pg_indexes"</span>,
        <span class="hljs-symbol">select:</span> %{<span class="hljs-symbol">index_name:</span> p.indexname, <span class="hljs-symbol">table_name:</span> p.tablename},
        <span class="hljs-symbol">where:</span> p.schemaname == <span class="hljs-string">"public"</span>

    table_headers = %{<span class="hljs-symbol">index_name:</span> <span class="hljs-string">"Index Name"</span>, <span class="hljs-symbol">table_name:</span> <span class="hljs-string">"Table Name"</span>}
    rows = Repo.all(query)
    widest_column_counts = longest_field_values(rows)

    <span class="hljs-comment"># To render Table header</span>
    render_single_row(table_headers, widest_column_counts)

    <span class="hljs-comment"># render Table header bottom  border</span>
    IO.puts(<span class="hljs-string">"| <span class="hljs-subst">#{String.duplicate(<span class="hljs-string">"-"</span>, Enum.sum(widest_column_counts) + <span class="hljs-number">9</span>)}</span> |"</span>)

    <span class="hljs-comment"># render Table rows</span>
    render_multiple_rows(rows, widest_column_counts)
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment"># Helper functions</span>
  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_multiple_rows</span></span>(rows, widest_column_counts) <span class="hljs-keyword">do</span>
    Enum.each(rows, <span class="hljs-keyword">fn</span> row -&gt; render_single_row(row, widest_column_counts) <span class="hljs-keyword">end</span>)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_single_row</span></span>(%{<span class="hljs-symbol">index_name:</span> index_name, <span class="hljs-symbol">table_name:</span> table} , widest_column_counts) <span class="hljs-keyword">do</span>
    IO.write(<span class="hljs-string">"| "</span>)

    [index_name, table]
      |&gt; Enum.with_index()
      |&gt; Enum.each(<span class="hljs-keyword">fn</span> {column_content, index} -&gt;
          border = render_border(index, column_content, widest_column_counts)
          IO.write(<span class="hljs-string">"<span class="hljs-subst">#{item}</span> <span class="hljs-subst">#{border}</span>"</span>)
        <span class="hljs-keyword">end</span>)

    IO.puts(<span class="hljs-string">" "</span>) <span class="hljs-comment">#Next line</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">render_border</span></span>(position_of_column, item, columns_count) <span class="hljs-keyword">do</span>
    count = Enum.at(columns_count, position_of_column) - String.length(item)
    <span class="hljs-string">"<span class="hljs-subst">#{String.duplicate(<span class="hljs-string">" "</span>, count)}</span> | "</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">find_longest_length</span></span>(rows) <span class="hljs-keyword">do</span>
    Enum.reduce(rows, [], <span class="hljs-keyword">fn</span> row, acc -&gt;
      Enum.zip(row, acc ++ [0])
      |&gt; Enum.map(<span class="hljs-keyword">fn</span> {str, len} -&gt;
        String.length(str) |&gt; Kernel.max(len)
      <span class="hljs-keyword">end</span>)
    <span class="hljs-keyword">end</span>)
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">longest_field_values</span></span>(records) <span class="hljs-keyword">do</span>
    Enum.map(records, <span class="hljs-keyword">fn</span> index -&gt;
      [index.index_name, index.table_name]
    <span class="hljs-keyword">end</span>)
    |&gt; find_longest_length
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>This task may seem a little absurd, but my motivation was to avoid and save some clicks and switching between terminals. We can tweak the code a little further and pass flags to the command, something like <code>--table_name</code> and <code>--first_50/--last_50</code>, to render the first 50 or last 50 records of the passed `table_name`. I felt like sharing this in the hope that it might benefit someone. I hope you like this blog. If you have any questions, please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Adding PDF generate feature in Phoenix LiveView app]]></title><description><![CDATA[Generating pdf is very common in day-to-day web applications. In E-commerce websites like Amazon when we do any shopping we get the option to get the invoice. It's not an easy task to implement it people often have a hard time implementing it because...]]></description><link>https://abulasar.com/adding-pdf-generate-feature-in-phoenix-liveview-app</link><guid isPermaLink="true">https://abulasar.com/adding-pdf-generate-feature-in-phoenix-liveview-app</guid><category><![CDATA[chromicpdf]]></category><category><![CDATA[phoenix liveview]]></category><category><![CDATA[pdf]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Tue, 09 Jan 2024 12:51:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704802202987/9b51776e-1858-4fd1-936b-54df4efc0ba8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Generating pdf is very common in day-to-day web applications. In E-commerce websites like Amazon when we do any shopping we get the option to get the invoice. It's not an easy task to implement it people often have a hard time implementing it because of different dependencies issues. You can implement it easily with Phoenix LiveView because of the awesome Elixir ecosystem. In this blog, I am going to explain every step that is required to implement the feature to generate and download <code>PDF</code>. So, let's get started.</p>
<h3 id="heading-creating-a-phoenix-liveview-project">Creating a Phoenix LiveView Project</h3>
<p>First of all, we should have a LiveView project. If you have one skip this section and continue <code>Installing Chromic PDF</code> section. If you don't have one then create a new LiveView project by running <code>mix phx.new my_app --live</code>. We will then navigate to the project directory by running cd <code>my_app</code> in the terminal. And finally, run the project running <code>mix phx.server</code> in the terminal. Once, the project starts running navigate to localhost:4000 to see the "Welcome phoenix" page.</p>
<h3 id="heading-installing-chromic-pdf">Installing Chromic PDF</h3>
<p>To create a PDF, the Elixir community has an awesome library called <a target="_blank" href="https://github.com/bitcrowd/chromic_pdf">chromic_pdf</a>. It is very easy to integrate. It does not use <code>puppeteer</code>, and hence does not require Node.js. It communicates directly with Chrome's DevTools API over pipes, offering the same performance as <code>puppeteer</code>, if not better. So we are going to use it. Let's add it to our project. Open the <code>mix.exs</code> file and make an entry of it as shown below.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">deps</span></span> <span class="hljs-keyword">do</span>
  ...
  {<span class="hljs-symbol">:chromic_pdf</span>, <span class="hljs-string">"~&gt; 1.14"</span>},
  ...
<span class="hljs-keyword">end</span>
</code></pre>
<p>Then install the dependency by running <code>mix deps.get</code>. It will install all the dependencies. Now, we will run <code>ChromicPDF</code> as part of our application by adding it in <code>application.ex</code>.</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">MyApp.Application</span></span> <span class="hljs-keyword">do</span>
   ...
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span></span>(_type, _args) <span class="hljs-keyword">do</span>
    children = [
      ...
      ChromicPDF
      ...
    ]
    ...
  <span class="hljs-keyword">end</span>
...
<span class="hljs-keyword">end</span>
</code></pre>
<p>This will make sure that this process should be started when the application starts and is supervised by the Supervisor.</p>
<h3 id="heading-adding-endpoint-to-generate-pdf">Adding endpoint to generate PDF</h3>
<p>We want our pdf generator to be served at a different endpoint and serve a regular HTTP response because we cannot send <code>pdf</code> over a web socket i.e. LiveView. We will add a <code>GET</code> endpoint <code>/generate-invoice</code> under <code>BillsController</code>.</p>
<pre><code class="lang-javascript">scope <span class="hljs-string">"/"</span>, MyAppWeb <span class="hljs-keyword">do</span>
    pipe_through [:browser, :require_authenticated_distributor]

    get <span class="hljs-string">"/generate-invoice"</span>, BillsController, :index
end
</code></pre>
<p>Make sure to add the <code>require_authenticated_distributor</code> plug (or any other authentication/authorization plug) in the pipeline to make sure the endpoint is authenticated else it will be exposed to the attackers.</p>
<h3 id="heading-adding-controller">Adding Controller</h3>
<p>Now, we will add the controller for the endpoint we added in the router</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">MyAppWeb.BillsController</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> MyAppWeb, <span class="hljs-symbol">:controller</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span></span>(conn, _params) <span class="hljs-keyword">do</span>
    <span class="hljs-comment">#Assign some attributes and values which we can use in the pdf.</span>
    conn = assign(conn, <span class="hljs-symbol">:total_amount_paid</span>, <span class="hljs-number">10000</span>) 
    {<span class="hljs-symbol">:ok</span>, pdf} = to_pdf(conn.assigns) <span class="hljs-comment">### We will write this function later.</span>

    send_download(
        conn,
        {<span class="hljs-symbol">:binary</span>, Base.decode64!(pdf)},
        <span class="hljs-symbol">content_type:</span> <span class="hljs-string">"application/pdf"</span>,
        <span class="hljs-symbol">filename:</span> <span class="hljs-string">"invoice"</span>
    )
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>At the end of the <code>index</code> action, we can see the <code>send_download</code> button which takes 4 parameters:</p>
<ul>
<li><p><code>conn:</code> a connection struct.</p>
</li>
<li><p><code>content</code> in binary tuple format i.e.<code>pdf</code> in this case.</p>
</li>
<li><p><code>content_type</code>: the content type.</p>
</li>
<li><p><code>filename</code>: give the file name of our choice.</p>
</li>
</ul>
<p><code>send_download</code> is used to send the given file or binary as a download. In other words, it will be the reason to download the generated PDF in your browser.</p>
<h3 id="heading-implementing-topdf-function">Implementing <code>to_pdf</code> function</h3>
<p>In the <code>index</code> action, we saw the <code>to_pdf</code> function in the previous section. We are going to implement it in this section. In this function, we will see <code>ChromicPDF</code> in action. You will see two functions <code>source_and_options</code> and <code>print_to_pdf</code>.</p>
<ul>
<li><p><code>source_and_options:</code> The module <code>Template</code> in <code>ChromicPDf</code> usage is mainly to generate HTML from the provided template and styling. We can see it calls the <code>source_and_options</code> function which returns the source and options for a PDF to be printed, as per the given set of template options like <code>content</code> and <code>size</code> of the PDF.</p>
</li>
<li><p><code>print_to_pdf:</code> As the name suggests it Prints a PDF. It is a blocking call which means it will block further code until the PDF is generated.</p>
</li>
</ul>
<p>Refer to the following code</p>
<pre><code class="lang-javascript">def to_pdf(assigns) <span class="hljs-keyword">do</span>
    [
      content: content(assigns), # <span class="hljs-string">`content`</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">yet</span> <span class="hljs-title">to</span> <span class="hljs-title">be</span> <span class="hljs-title">implemented</span>
      <span class="hljs-title">size</span>: :<span class="hljs-title">a4</span>,
    ]
    |&gt; <span class="hljs-title">ChromicPDF</span>.<span class="hljs-title">Template</span>.<span class="hljs-title">source_and_options</span>(<span class="hljs-params"></span>)
    |&gt; <span class="hljs-title">ChromicPDF</span>.<span class="hljs-title">print_to_pdf</span>(<span class="hljs-params"></span>)
<span class="hljs-title">end</span></span>
</code></pre>
<h3 id="heading-generating-content-for-the-pdf">Generating content for the PDF</h3>
<p>Now, the most important part of the implementation. You have seen the <code>content</code> function in <code>to_pdf</code> which is yet to be implemented. We are going to implement that and learn why we are going to use <code>components</code> and not <code>views</code>. Earlier, <code>Phoenix.View</code> was used to render HTML (view) which was later passed to the <code>ChromicPDF#source_and_options</code> function. But since <code>Phoenix 1.7</code> the <code>Phoenix.View</code> has been eliminated and substituted with the new <code>Phoenix.Template</code>. Phoenix 1.7 supports function components to render both controller-based and LiveView-based components. So, we are going to use <code>Phoenix.LiveComponent</code> instead of <code>Phoenix.View</code>. For this, we will create a component <code>PdfRenderer</code> component inside the <code>components</code> folder, and in the module, we will add a <code>render</code> function. In this function we will write the HTML and CSS we want to render for the PDF. Refer to the following code.</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">PdfRendererComponent</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> Phoenix.LiveComponent

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">render</span></span>(assigns) <span class="hljs-keyword">do</span>
    <span class="hljs-string">~H"""
      &lt;h1&gt;Invoice&lt;/h1&gt;
      &lt;p style="</span>font-<span class="hljs-symbol">size:</span> <span class="hljs-number">200</span>%;<span class="hljs-string">"&gt;Total:  &lt;%= @total_amount_paid %&gt; &lt;/p&gt;
    "</span><span class="hljs-string">""</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>In the above <code>render</code> function you can see the field <code>total_amount_paid</code> attribute. Where does this come from? If you remember in the controller we assigned the <code>total_amount_paid</code> attribute to the <code>conn</code> struct. We are getting it here because of the <code>assign</code> parameter in the <code>render</code> function. To access it in this component we have to pass the <code>conn.assign</code> struct directly to the component. Something like this,</p>
<pre><code class="lang-javascript">PdfRendererComponent.render(assigns)
</code></pre>
<p>We will implement this function <code>content</code> to render this component. Refer to the following code.</p>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">content</span></span>(assigns) <span class="hljs-keyword">do</span>
    Phoenix.HTML.Safe.to_iodata(PdfRendererComponent.render(assigns))
<span class="hljs-keyword">end</span>
</code></pre>
<p>In Phoenix, you can call the functions of the components using <code>Phoenix.HTML.Safe.to_iodata</code>. The official documentation says <strong>"(It) provides better performance when sending or streaming data to the client"</strong>. So, in this case, the HTML is sent to the <code>ChromicPDF.Template.source_and_options()</code> to render. Whatever HTML and CSS we write in the render function will be passed to the <code>source_and_options</code>. The final working code of the controller is below.</p>
<pre><code class="lang-elixir"><span class="hljs-class"><span class="hljs-keyword">defmodule</span> <span class="hljs-title">MyAppWeb.BillsController</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> MyAppWeb, <span class="hljs-symbol">:controller</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span></span>(conn, _params) <span class="hljs-keyword">do</span>
    <span class="hljs-comment">#Assign some attributes and values which we can use in the pdf.</span>
    conn = assign(conn, <span class="hljs-symbol">:total_amount_paid</span>, <span class="hljs-number">10000</span>) 
    {<span class="hljs-symbol">:ok</span>, pdf} = to_pdf(conn.assigns) <span class="hljs-comment">### We will write this function later.</span>

    send_download(
        conn,
        {<span class="hljs-symbol">:binary</span>, Base.decode64!(pdf)},
        <span class="hljs-symbol">content_type:</span> <span class="hljs-string">"application/pdf"</span>,
        <span class="hljs-symbol">filename:</span> <span class="hljs-string">"invoice"</span>
    )
  <span class="hljs-keyword">end</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">to_pdf</span></span>(assigns) <span class="hljs-keyword">do</span>
    [
      <span class="hljs-symbol">content:</span> content(assigns), 
      <span class="hljs-symbol">size:</span> <span class="hljs-symbol">:a4</span>,
    ]
    |&gt; ChromicPDF.Template.source_and_options()
    |&gt; ChromicPDF.print_to_pdf()
 <span class="hljs-keyword">end</span>

 <span class="hljs-function"><span class="hljs-keyword">defp</span> <span class="hljs-title">content</span></span>(assigns) <span class="hljs-keyword">do</span>
    Phoenix.HTML.Safe.to_iodata(PdfRendererComponent.render(assigns))
 <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-adding-download-button">Adding Download button</h3>
<p>Finally, we need to add an <code>Invoice/Download</code> button to call this endpoint we created. It will be a simple <code>link</code> tag.</p>
<pre><code class="lang-javascript">&lt;%= link <span class="hljs-string">"Invoice"</span>, <span class="hljs-attr">to</span>: <span class="hljs-string">"/generate-invoice"</span>, <span class="hljs-attr">class</span>: <span class="hljs-string">"button"</span>, <span class="hljs-attr">download</span>: <span class="hljs-string">'invoice.pdf` %&gt;</span>
</code></pre>
<p>You’ll notice I have added an <code>download</code> attribute to the link tag. I wrote a small <a target="_blank" href="https://abulasar.com/til-add-the-download-attribute-on-the-link-tag-when-calling-the-download-endpoint">TIL</a> blog on its usage you can check it later 😊.</p>
<p>I hope you like this blog. If you have any questions please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://github.com/bitcrowd/chromic_pdf">Chromic PDF</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[TIL: How to add a form in a table in Phoenix LiveView?]]></title><description><![CDATA[Suppose, we want to add an inline edit feature on a table row. It can be a little tricky to implement. I had a hard time implementing it because I wasn't about one of the concepts of HTML that we cannot add a form tag inside a table. Something like t...]]></description><link>https://abulasar.com/til-how-to-add-a-form-in-a-table-in-phoenix-liveview</link><guid isPermaLink="true">https://abulasar.com/til-how-to-add-a-form-in-a-table-in-phoenix-liveview</guid><category><![CDATA[forms]]></category><category><![CDATA[today i learn ]]></category><category><![CDATA[phoenix liveview]]></category><category><![CDATA[Phoenix framework]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Thu, 04 Jan 2024 08:11:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ldDmTgf89gU/upload/2baa9dcf4e1bb12314e41e77cc04984c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Suppose, we want to add an inline edit feature on a table row. It can be a little tricky to implement. I had a hard time implementing it because I wasn't about one of the concepts of HTML that we cannot add a <code>form</code> tag inside a <code>table</code>. Something like this</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>
  .....
  .....
  .....
  <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"count"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rate"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"paid"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
</code></pre>
<p>This wasn't working because placing a form as a child element of a <code>table</code>, <code>tbody</code>, or <code>tr</code> is not allowed. If we add it the browser typically relocates the form to appear after the table, leaving its contents, such as table rows, cells, inputs, etc., in their original positions within the table. Hence, when we try to submit the form it won't work.</p>
<h3 id="heading-how-can-we-fix-this-problem">How can we fix this problem?</h3>
<p>To solve this problem we can use the <code>form</code> attribute. For this, we will place an empty form somewhere within the page and outside of the <code>table</code> tag. We need to provide the form an <code>id</code> attribute. This <code>id</code> will be an identifier for the form fields. Something like this</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">.simple_form</span> <span class="hljs-attr">for</span>=<span class="hljs-string">{@distribution_record_form}</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"distribution_record"</span> <span class="hljs-attr">phx-submit</span>=<span class="hljs-string">"save"</span> <span class="hljs-attr">phx-update</span>=<span class="hljs-string">"ignore"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex"</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">.simple_form</span>&gt;</span>
</code></pre>
<p>And later inside the table for each field i.e. cell we can add the <code>form</code> attribute to tell the fields to which form it belongs. It should be the same as the <code>id</code> of the <code>form</code> .</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">.input</span> <span class="hljs-attr">field</span>=<span class="hljs-string">{@distribution_record_form[:rate]}</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"distribution_record"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
</code></pre>
<p>The <code>form</code> attribute will tell the <code>input</code> field that it is part of <code>distribution_record</code> <code>form</code>. So, when the user tries to submit the form whatever data the user will add in this input box will be part of the <code>distribution_record</code> form. Our requirement is that each row i.e. <code>tr</code> should appear as a form in which the last column will have a <code>submit</code> button. So below is the code of how each row will appear.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">.input</span> <span class="hljs-attr">field</span>=<span class="hljs-string">{@distribution_record_form[:count]}</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"distribution_record"</span> /&gt;</span> 
     <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">.input</span> <span class="hljs-attr">field</span>=<span class="hljs-string">{@distribution_record_form[:rate]}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"distribution_record"</span> /&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">.input</span> <span class="hljs-attr">field</span>=<span class="hljs-string">{@distribution_record_form[:paid]}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"distribution_record"</span> /&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">.button</span> <span class="hljs-attr">phx-disable-with</span>=<span class="hljs-string">"Saving..."</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"distribution_record"</span>&gt;</span>
            Update
        <span class="hljs-tag">&lt;/<span class="hljs-name">.button</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
</code></pre>
<p>The above code will map to the <code>form</code> one that we created earlier. The below diagram will give you an idea of how this mapping is going to work.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704354780054/c769f568-c631-431c-b082-ba1a05409c96.png" alt class="image--center mx-auto" /></p>
<p>I hope this diagram will make sense to you. I know this is quite a trivial thing but I wasn't aware until I came across this problem and I noted it in my TIL list and shared it in this blog. I hope you like this <code>TIL</code> blog. If you have any questions please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[TIL: Add the "download" attribute on the link tag when calling the download endpoint]]></title><description><![CDATA[Downloading pdf or any other documents is very common in day-to-day web applications. We often see buttons like Download, Generate Invoice, etc buttons in web applications that makes calls to a GET endpoint which does some computations and downloads ...]]></description><link>https://abulasar.com/til-add-the-download-attribute-on-the-link-tag-when-calling-the-download-endpoint</link><guid isPermaLink="true">https://abulasar.com/til-add-the-download-attribute-on-the-link-tag-when-calling-the-download-endpoint</guid><category><![CDATA[today i learn ]]></category><category><![CDATA[download]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[phoenix liveview]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Wed, 03 Jan 2024 09:03:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746034906641/22ed43f6-e8fc-44b6-98d7-dbd0cf55b995.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Downloading pdf or any other documents is very common in day-to-day web applications. We often see buttons like <code>Download</code>, <code>Generate Invoice</code>, etc buttons in web applications that makes calls to a <code>GET</code> endpoint which does some computations and downloads <code>pdf</code> or any other document file. </p>
<h3 id="heading-problem">Problem</h3>
<p>While working on one of the projects I added an <code>endpoint</code> i.e. <code>/generate-invoice</code>(GET) in a <code>Phoenix</code> application. Its job was to do some tabular computation and generate a PDF. I added a link tag to call the endpoint something like this </p>
<pre><code class="lang-elixir">&lt;%= link <span class="hljs-string">"Invoice"</span>, <span class="hljs-symbol">to:</span> <span class="hljs-string">"/generate-invoice"</span>, <span class="hljs-symbol">class:</span> <span class="hljs-string">"button"</span> %&gt;
</code></pre>
<p>As expected it was working fine but later I noticed a weird behavior. After clicking the <code>Invoice</code> button when I tried to click other buttons or any action that involved javascript, it wasn't working. I thought there was some bug with my endpoint or other code I tried to fix it but things weren't working for me.</p>
<h3 id="heading-solution">Solution</h3>
<p>When we try to access a <code>GET</code> endpoint that downloads, the browser navigates away from the current page. It doesn‘t know the target will download something, and will therefore shut down the javascript of the current page. Till then I wasn't aware of the <code>download</code> attribute on the <code>link</code> tag. Adding the <code>download</code> attribute on the above link tag helped me in fixing the problem.</p>
<pre><code class="lang-elixir">&lt;%= link <span class="hljs-string">"Invoice"</span>, <span class="hljs-symbol">to:</span> <span class="hljs-string">"/generate-invoice"</span>, <span class="hljs-symbol">class:</span> <span class="hljs-string">"button"</span>, <span class="hljs-symbol">download:</span> <span class="hljs-string">'invoice.pdf` %&gt;</span>
</code></pre>
<p> I hope you like this small TIL blog. If you have any questions please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[TIL: Use the `useMaster` property to fetch data from the write pool in Sequelize.]]></title><description><![CDATA[Sequelize offers support for read replication, enabling the use of multiple servers for executing SELECT queries. In this configuration, you designate one or more servers as read replicas, while appointing one server as the primary writer. The primar...]]></description><link>https://abulasar.com/til-use-the-usemaster-property-to-fetch-data-from-the-write-pool-in-sequelize</link><guid isPermaLink="true">https://abulasar.com/til-use-the-usemaster-property-to-fetch-data-from-the-write-pool-in-sequelize</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Sequelize]]></category><category><![CDATA[replication]]></category><category><![CDATA[SQL]]></category><category><![CDATA[TIL]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Mon, 25 Dec 2023 21:44:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703540321163/21bdb0f6-9d35-4902-873e-4c5ef0f3ec97.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sequelize offers support for read replication, enabling the use of multiple servers for executing SELECT queries. In this configuration, you designate one or more servers as read replicas, while appointing one server as the primary writer. The primary writer manages all write and update operations, disseminating them to the designated replicas. It's important to note that Sequelize does not handle the actual replication process; this responsibility lies with the database backend, requiring proper setup.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> sequelize = <span class="hljs-keyword">new</span> Sequelize(<span class="hljs-string">'database'</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, {
  <span class="hljs-attr">dialect</span>: <span class="hljs-string">'mysql'</span>,
  <span class="hljs-attr">port</span>: <span class="hljs-number">3306</span>,
  <span class="hljs-attr">replication</span>: {
    <span class="hljs-attr">read</span>: [
      { <span class="hljs-attr">host</span>: <span class="hljs-string">'8.8.8.8'</span>, <span class="hljs-attr">username</span>: <span class="hljs-string">'read-1-username'</span>, <span class="hljs-attr">password</span>: process.env.READ_DB_1_PW },
      { <span class="hljs-attr">host</span>: <span class="hljs-string">'9.9.9.9'</span>, <span class="hljs-attr">username</span>: <span class="hljs-string">'read-2-username'</span>, <span class="hljs-attr">password</span>: process.env.READ_DB_2_PW }
    ],
    <span class="hljs-attr">write</span>: { <span class="hljs-attr">host</span>: <span class="hljs-string">'1.1.1.1'</span>, <span class="hljs-attr">username</span>: <span class="hljs-string">'write-username'</span>, <span class="hljs-attr">password</span>: process.env.WRITE_DB_PW }
  },
  <span class="hljs-attr">pool</span>: { 
    <span class="hljs-attr">max</span>: <span class="hljs-number">20</span>,
    <span class="hljs-attr">idle</span>: <span class="hljs-number">30000</span>
  },
})
</code></pre>
<p>Recently, I came across a situation where I was creating(writing) data and immediately on the next line was returning the created record along with some association as an API response but the record was returning empty.</p>
<pre><code><span class="hljs-keyword">const</span> recordIds = <span class="hljs-keyword">await</span> fileService.create(req.files)
<span class="hljs-keyword">const</span> files = <span class="hljs-keyword">await</span> fileService.list({ <span class="hljs-attr">id</span>: recordIds })
<span class="hljs-keyword">return</span> response(res, {<span class="hljs-attr">record</span>: files})
</code></pre><p>The <code>fileService.create</code> function creates record file records in the database and returns an array of <code>ids</code> of the files created. In the next line i.e. the <code>fileService.list</code> function takes the array of IDs, fetches the record, and returns an API response in the last line. Below is the implementation of the <code>list</code> function</p>
<pre><code><span class="hljs-keyword">async</span> list(ids) {
   <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Model.findAll({
      <span class="hljs-attr">where</span>: {
        <span class="hljs-attr">id</span>: ids
      },
      ...associationQuery
    })
}
</code></pre><p>This looks plain and simple but for some reason, the <code>list</code> function didn't return any record. I did a lot of debugging and everything looked fine. Then I came across the property <code>useMaster</code>. The read replication we read about in our introduction takes some time to replicate in the <code>read</code> pool. The <code>write</code> or <code>useMaster: true</code> query uses a write pool to create or fetch data. So, the <code>useMaster: true</code> property fixes the issue of fetching data immediately after creating and it fulfills my requirement. Hence, I added <code>useMaster: true</code> in the above query. So the above <code>list</code> function becomes like this 👇</p>
<pre><code><span class="hljs-keyword">async</span> list(ids) {
   <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Model.findAll({
      <span class="hljs-attr">where</span>: {
        <span class="hljs-attr">id</span>: ids
      },
      ...associationQuery,
      <span class="hljs-attr">useMaster</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// New added line</span>
    })
}
</code></pre><p>The above code fixed the issue of data not being fetched in read replicas. I hope you like this small <code>TIL</code> blog. If you have any questions please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://sequelize.org/docs/v6/other-topics/read-replication/">Read Replication in Sequelize</a></li>
<li><a target="_blank" href="https://avikdas.com/assets/images/2020-04-13-scalability-concepts-read-after-write-consistency/write-then-read-from-different-source.png">Cover Image</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Creating a React component to render normal SVG in react-pdf.]]></title><description><![CDATA[Recently, while working on a React project, I encountered a bug (or requirement) where SVG images weren't getting exported in PDF. The PDF export feature was an existing feature developed earlier by one of my colleagues in the project. He developed t...]]></description><link>https://abulasar.com/creating-a-react-component-to-render-normal-svg-in-react-pdf</link><guid isPermaLink="true">https://abulasar.com/creating-a-react-component-to-render-normal-svg-in-react-pdf</guid><category><![CDATA[React]]></category><category><![CDATA[#react-pdf]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Mon, 25 Dec 2023 19:58:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703532027102/038a0ba2-8dce-4d6b-84ae-7db83f9b4ba1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, while working on a React project, I encountered a bug (or requirement) where SVG images weren't getting exported in PDF. The PDF export feature was an existing feature developed earlier by one of my colleagues in the project. He developed this PDF export feature using <code>react-pdf</code>. As per the requirement, only <code>jpeg</code> and <code>png</code> images were considered for export in PDF, which works fine for react-pdf. Everything was working fine until the requirement changed and users were allowed to upload SVG images in the application. Since the SVG image was allowed to be uploaded, it was expected that the SVG should be exported in the PDF as well. This is where things got out of hand. Normal SVGs don't get rendered in react-pdf. React-pdf has its DSL (syntax) to render SVG images. This blog post will explain how I solved this problem of rendering normal SVGs into react-pdf.</p>
<h3 id="heading-what-do-we-want-to-achieve-an-example">What do we want to achieve? (An example)</h3>
<p>Suppose, we have the following SVG string which we want to export on pdf.</p>
<pre><code class="lang-elixir">&lt;svg xmlns=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> viewBox=<span class="hljs-string">"0 0 100 100"</span> fill=<span class="hljs-string">"none"</span> stroke=<span class="hljs-string">"#000"</span> stroke-width=<span class="hljs-string">"4"</span> aria-label=<span class="hljs-string">"Chicken"</span>&gt;
    &lt;path d=<span class="hljs-string">"M48.1 34C22.1 32 1.4 51 2.5 67.2c1.2 16.1 19.8 17 29 17.8H89c15.7-6.6 6.3-18.9.3-20.5A28 28 0 0073 41.7c-.5-7.2 3.4-11.6 6.9-15.3 8.5 2.6 8-8 .8-7.2.6-6.5-12.3-5.9-6.7 2.7l-3.7 5c-6.9 5.4-10.9 5.1-22.2 7zM48.1 34c-38 31.9 29.8 58.4 25 7.7M70.3 26.9l5.4 4.2"</span>/&gt;
&lt;<span class="hljs-regexp">/svg&gt;</span>
</code></pre>
<p>We can see in the above code snippet there are two main tags. One is the main <code>&lt;svg&gt;</code> tag and the other is the <code>&lt;path&gt;</code> tag. Now we are going to refer to <code>react-pdf</code> documentation under the <a target="_blank" href="https://react-pdf.org/svg"><code>SVG</code></a> section. We can see we have both tags under the SVG section. For the <code>svg</code> it is a <code>&lt;Svg /&gt;</code> tag and for <code>path</code> it is the <code>&lt;Path&gt;</code> tag. So the above SVG string will become as follows</p>
<pre><code class="lang-elixir">&lt;Svg xmlns=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> viewBox=<span class="hljs-string">"0 0 100 100"</span> fill=<span class="hljs-string">"none"</span> stroke=<span class="hljs-string">"#000"</span> stroke-width=<span class="hljs-string">"4"</span> aria-label=<span class="hljs-string">"Chicken"</span>&gt;
    &lt;Path d=<span class="hljs-string">"M48.1 34C22.1 32 1.4 51 2.5 67.2c1.2 16.1 19.8 17 29 17.8H89c15.7-6.6 6.3-18.9.3-20.5A28 28 0 0073 41.7c-.5-7.2 3.4-11.6 6.9-15.3 8.5 2.6 8-8 .8-7.2.6-6.5-12.3-5.9-6.7 2.7l-3.7 5c-6.9 5.4-10.9 5.1-22.2 7zM48.1 34c-38 31.9 29.8 58.4 25 7.7M70.3 26.9l5.4 4.2"</span> /&gt;
&lt;<span class="hljs-regexp">/Svg&gt;</span>
</code></pre>
<h3 id="heading-converting-an-image-from-storage-url-to-svg-string">Converting an image from storage URL to SVG string</h3>
<p>The main requirement to use react-pdf SVG is that we need to have normal SVG as a string. But in most cases and even in our case as well, we are getting these images i.e. jpg, png, and svg as <code>URL</code> from the s3 bucket. We need to fetch these images blob and convert it into the SVG string. So, the steps to achieve this are as follows.</p>
<ul>
<li><p>First, fetch the image bytes from the API endpoint.</p>
</li>
<li><p>Then convert it to <code>text</code>. You can refer to the following code to achieve it.</p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(s3_bucket_url,
               { 
                <span class="hljs-attr">headers</span>: {<span class="hljs-string">"x-access-token"</span>: token}
               });
<span class="hljs-keyword">const</span> rawSVG = <span class="hljs-keyword">await</span> response.text();
<span class="hljs-keyword">const</span> photoContent = rawSVG;
</code></pre>
<h3 id="heading-creating-svg-renderer-component">Creating SVG Renderer component</h3>
<p>Now comes the meat of the blog i.e. component that will convert the fetched SVG string to a <code>react-pdf</code> specific SVG. We will create a component <code>SVGRenderer</code>. It will take <code>svgString</code> from the parent component where we are fetching the svgString.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SVGRenderer = <span class="hljs-function">(<span class="hljs-params">{ svgString }</span>) =&gt;</span> {
   <span class="hljs-comment">// Logic will be here</span>
}
</code></pre>
<p>The working of this component can be divided into 5 steps:</p>
<p><em>Step 1:</em> Convert the SVG string to a javascript object.</p>
<p><em>Step 2:</em> Convert each tag into a <code>react-pdf</code> tag</p>
<p><em>Step 3:</em> Convert styles into style objects compatible with React-Pdf</p>
<p><em>Step 4:</em> Create an object of each tag to the corresponding react-pdf SVG object.</p>
<p><em>Step 5:</em> Calling svgToJsx function.</p>
<h4 id="heading-step-1-convert-the-svg-string-to-a-javascript-object">Step 1 - Convert the SVG string to a javascript object</h4>
<ul>
<li><p>We want our SVG string to be converted into a Javascript object so that we can pluck different properties of SVG and use it to render <code>react-pdf</code> specific SVG.</p>
</li>
<li><p>For this, we are going to use the <code>svg-parser</code> library. The package says that <code>"(It)Takes a string representing an SVG document or fragment, turn it into</code><a target="_blank" href="https://github.com/syntax-tree/hast"><code>HAST</code></a><code>JavaScript object.</code></p>
</li>
<li><p>We will install the library</p>
</li>
</ul>
<pre><code class="lang-bash">  npm install svg-parser --save
</code></pre>
<ul>
<li>We will import the <code>svg-parser</code> into the component file and it will convert the string to an object.</li>
</ul>
<pre><code class="lang-javascript">   <span class="hljs-keyword">import</span> { parse <span class="hljs-keyword">as</span> svgparse } <span class="hljs-keyword">from</span> <span class="hljs-string">"svg-parser"</span>;

   <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SVGRenderer = <span class="hljs-function">(<span class="hljs-params">{ svgString }</span>) =&gt;</span> {
       <span class="hljs-keyword">const</span> parsedSVG = svgparse(svgString);
       .....
       .....
       .....
    }
</code></pre>
<h4 id="heading-step-2-convert-each-tag-into-a-react-pdf-tag">Step 2 - Convert each tag into a <code>react-pdf</code> tag</h4>
<p>Each SVG is made up of multiple nested tags. Like in our example in the introduction, there is an <code>svg</code> tag underneath there is a <code>path</code> tag. Each tag has its properties and styling. We have to write a recursive function that will pluck the properties, content, and styles of each tag. Later, we will use those properties to construct the <code>react-pdf</code> SVG object. Refer to the following pseudo-code you well get an idea.</p>
<pre><code class="lang-elixir">  const tag = tag.type === <span class="hljs-string">"element"</span> ? tag.tagName : tag.type;
  const props = {
    <span class="hljs-symbol">style:</span> convertStylesStringToObject(tag.properties.style), <span class="hljs-regexp">//</span> We are yet to implement this function
    <span class="hljs-symbol">key:</span> someUniqueKey,
    ...tag.properties
  }
 let children = tag.content
</code></pre>
<p>Later, we will use the above constructed props to create an SVG tag. Something like this👇</p>
<pre><code class="lang-elixir">if (tag === <span class="hljs-string">"svg"</span>) {
      <span class="hljs-keyword">return</span> &lt;Svg {...props}&gt;{children}&lt;<span class="hljs-regexp">/Svg&gt;;
}</span>
</code></pre>
<p>The above sample code gives us some idea about how we are going to create the function. We will name this function <code>svgToJsx</code> it will take two arguments. The first argument is the <code>tag</code> we will name it <code>obj</code> and the other tag is the <code>index</code> for the unique key. Refer to the following code.</p>
<pre><code class="lang-elixir">const svgToJsx = (obj, index) =&gt; {
    let name = obj.type === <span class="hljs-string">"element"</span> ? obj.tagName : obj.type;
    let props = { <span class="hljs-symbol">key:</span> index + name };

    if (obj.properties !== undefined) {
      if (obj.properties.style !== undefined) {
        props.style = convertStylesStringToObject(obj.properties.style); <span class="hljs-regexp">//</span> Yet to write
      }
      props = { ...obj.properties, ...props };
    }
    let children =
      obj.children !== undefined ? (
        obj.children.map((c, i) =&gt; {
          <span class="hljs-keyword">return</span> svgToJsx(c, index + <span class="hljs-string">"-"</span> + i);
        })
      ) : (
        &lt;&gt;&lt;<span class="hljs-regexp">/&gt;
      );
    ......
    ......
    ......
}</span>
</code></pre>
<h4 id="heading-step-3-convert-styles-into-style-objects-compatible-with-react-pdf">Step 3 - Convert styles into style objects compatible with React-Pdf</h4>
<p>You may have noticed the <code>convertStylesStringToObject</code> function mentioned in the <code>svgToJsx</code> function which is yet to be implemented. Normally in SVG, we write inline CSS as follows:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"fill:green; margin: 30px;"</span>&gt;</span>
</code></pre>
<p>Our target is to convert the style into an object as follows</p>
<pre><code class="lang-elixir">&lt;Circle style={{ <span class="hljs-symbol">fill:</span> <span class="hljs-string">'green'</span>, <span class="hljs-symbol">margin:</span> <span class="hljs-number">30</span> }} /&gt;
</code></pre>
<p>For this, we are going to write a reducer function that will convert the CSS string into a JSX compatible style object. It will do two tasks:</p>
<p>a) Remove spaces and semicolons</p>
<p>b) Convert two-word(dasherized) attributes into camel case For example, <code>stroke-width</code> will be converted to <code>strokeWidth</code>, or <code>background-color</code> will be converted to <code>backgroundColor</code>. Refer to the following code for reference.</p>
<pre><code class="lang-elixir">const convertStylesStringToObject = (stringStyles) =&gt; {
    let styles =
      typeof stringStyles === <span class="hljs-string">"string"</span> &amp;&amp; stringStyles !== undefined
        ? stringStyles
          .replaceAll(<span class="hljs-string">"&amp;quot;"</span>, <span class="hljs-string">"'"</span>)
          .split(<span class="hljs-string">";"</span>)
          .reduce((acc, style) =&gt; {
            const colonPosition = style.indexOf(<span class="hljs-string">":"</span>);

            if (colonPosition === <span class="hljs-number">-1</span>) <span class="hljs-keyword">return</span> acc;

            const camelCaseProperty = style
                .substr(0, colonPosition)
                .trim()
                .replace(<span class="hljs-regexp">/^-ms-/</span>, <span class="hljs-string">"ms-"</span>)
                .replace(<span class="hljs-regexp">/-./g</span>, (c) =&gt; c.substr(<span class="hljs-number">1</span>).toUpperCase()),
              value = style.substr(colonPosition + <span class="hljs-number">1</span>).trim();

            let isSvgStyle = [
              <span class="hljs-string">"color"</span>,
              <span class="hljs-string">"dominantBaseline"</span>,
              <span class="hljs-string">"fill"</span>,
              <span class="hljs-string">"fillOpacity"</span>,
              <span class="hljs-string">"fillRule"</span>,
              <span class="hljs-string">"opacity"</span>,
              <span class="hljs-string">"stroke"</span>,
              <span class="hljs-string">"strokeWidth"</span>,
              <span class="hljs-string">"strokeOpacity"</span>,
              <span class="hljs-string">"strokeLinecap"</span>,
              <span class="hljs-string">"strokeDasharray"</span>,
              <span class="hljs-string">"transform"</span>,
              <span class="hljs-string">"textAnchor"</span>,
              <span class="hljs-string">"visibility"</span>
            ].includes(camelCaseProperty);

            <span class="hljs-keyword">return</span> isSvgStyle &amp;&amp; value ? { 
              ...acc, 
              [camelCaseProperty]: value 
            } : acc;
          }, {}) 
        : {};

    <span class="hljs-keyword">return</span> styles;
  };
</code></pre>
<h4 id="heading-step-4-pass-created-svg-attributes-props-and-styles-to-react-pdf-svg-tag">Step 4 - Pass created SVG attributes, props, and styles to <code>react-pdf</code> SVG tag</h4>
<p>We've already seen an example in the introduction where we converted the <code>svg</code> and <code>path</code> to <code>Svg</code> and <code>Path</code>. So, in the final part of the <code>svgToJsx</code> function, we will convert each tag into a <code>react-pdf</code> specific tag as per the <code>name</code> and will apply styles and content.</p>
<pre><code class="lang-elixir">   if (name === <span class="hljs-string">"svg"</span>) {
      <span class="hljs-keyword">return</span> &lt;Svg {...props}&gt;{children}&lt;<span class="hljs-regexp">/Svg&gt;;
   }</span>
</code></pre>
<p>Also, we will import all the SVG tags in the component.</p>
<pre><code class="lang-elixir"><span class="hljs-keyword">import</span> {
  Svg,
  Line,
  Polyline,
  Polygon,
  Path,
  Rect,
  Circle,
  Ellipse,
  Text,
  Tspan,
  G,
  Stop,
  Defs,
  ClipPath,
  LinearGradient,
  RadialGradient
} from <span class="hljs-string">"@react-pdf/renderer"</span>;
</code></pre>
<h4 id="heading-step-5-calling-svgtojsx-function">Step 5: Calling svgToJsx function</h4>
<p>In the last step <code>SvgRenderer</code> component will return the <code>svgToJsx</code> function and it will pass <code>parsedSVG.children[0]</code> and an initial index value of <code>0</code> as parameters. Refer to the code snippet below.</p>
<pre><code class="lang-elixir">  <span class="hljs-keyword">return</span> &lt;&gt;
    {svgToJsx(parsedSVG.children[0], 0)}
  &lt;<span class="hljs-regexp">/&gt;</span>
</code></pre>
<h3 id="heading-passing-the-svg-string-to-the-component">Passing the SVG string to the component</h3>
<p>Now, we understand the working of the <code>SVGRenderer</code> component. The only step that is remaining is to pass svg string we fetched from the API to this component. Refer to the following code.</p>
<pre><code class="lang-javascript"> &lt;SVGRenderer svgString={photoContent} /&gt;
</code></pre>
<p>This completes the entire code explanation of converting SVG to <code>react-pdf</code> specific SVG. Below is the complete code of the <code>SVGRender</code> component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> {
  Svg,
  Line,
  Polyline,
  Polygon,
  Path,
  Rect,
  Circle,
  Ellipse,
  Text,
  Tspan,
  G,
  Stop,
  Defs,
  ClipPath,
  LinearGradient,
  RadialGradient
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-pdf/renderer"</span>;
<span class="hljs-keyword">import</span> { parse <span class="hljs-keyword">as</span> svgparse } <span class="hljs-keyword">from</span> <span class="hljs-string">"svg-parser"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SVGRenderer = <span class="hljs-function">(<span class="hljs-params">{ svgString }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> svgWithoutPixel = svgString.replaceAll(<span class="hljs-string">"px"</span>, <span class="hljs-string">"pt"</span>);
  <span class="hljs-keyword">const</span> parsedSVG = svgparse(svgWithoutPixel);

  <span class="hljs-keyword">const</span> convertStylesStringToObject = <span class="hljs-function">(<span class="hljs-params">stringStyles</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> styles =
      <span class="hljs-keyword">typeof</span> stringStyles === <span class="hljs-string">"string"</span> &amp;&amp; stringStyles !== <span class="hljs-literal">undefined</span>
        ? stringStyles
          .replaceAll(<span class="hljs-string">"&amp;quot;"</span>, <span class="hljs-string">"'"</span>)
          .split(<span class="hljs-string">";"</span>)
          .reduce(<span class="hljs-function">(<span class="hljs-params">acc, style</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> colonPosition = style.indexOf(<span class="hljs-string">":"</span>);

            <span class="hljs-keyword">if</span> (colonPosition === <span class="hljs-number">-1</span>) <span class="hljs-keyword">return</span> acc;

            <span class="hljs-keyword">const</span> camelCaseProperty = style
                .substr(<span class="hljs-number">0</span>, colonPosition)
                .trim()
                .replace(<span class="hljs-regexp">/^-ms-/</span>, <span class="hljs-string">"ms-"</span>)
                .replace(<span class="hljs-regexp">/-./g</span>, <span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.substr(<span class="hljs-number">1</span>).toUpperCase()),
              value = style.substr(colonPosition + <span class="hljs-number">1</span>).trim();

            <span class="hljs-keyword">let</span> isSvgStyle = [
              <span class="hljs-string">"color"</span>,
              <span class="hljs-string">"dominantBaseline"</span>,
              <span class="hljs-string">"fill"</span>,
              <span class="hljs-string">"fillOpacity"</span>,
              <span class="hljs-string">"fillRule"</span>,
              <span class="hljs-string">"opacity"</span>,
              <span class="hljs-string">"stroke"</span>,
              <span class="hljs-string">"strokeWidth"</span>,
              <span class="hljs-string">"strokeOpacity"</span>,
              <span class="hljs-string">"strokeLinecap"</span>,
              <span class="hljs-string">"strokeDasharray"</span>,
              <span class="hljs-string">"transform"</span>,
              <span class="hljs-string">"textAnchor"</span>,
              <span class="hljs-string">"visibility"</span>
            ].includes(camelCaseProperty);

            <span class="hljs-keyword">return</span> isSvgStyle &amp;&amp; value ? { 
              ...acc, 
              [camelCaseProperty]: value 
            } : acc;
          }, {}) 
        : {};

    <span class="hljs-keyword">return</span> styles;
  };

  <span class="hljs-comment">/**
   * Rendering logic to convert normal SVG (svgString) to react-pdf compatible SVG
   * **/</span> 
  <span class="hljs-keyword">const</span> svgToJsx = <span class="hljs-function">(<span class="hljs-params">obj, index</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> name = obj.type === <span class="hljs-string">"element"</span> ? obj.tagName : obj.type;
    <span class="hljs-keyword">let</span> props = { <span class="hljs-attr">key</span>: index + name };

    <span class="hljs-keyword">if</span> (obj.properties !== <span class="hljs-literal">undefined</span>) {
      <span class="hljs-keyword">if</span> (obj.properties.style !== <span class="hljs-literal">undefined</span>) {
        props.style = convertStylesStringToObject(obj.properties.style);
      }
      props = { ...obj.properties, ...props };
    }
    <span class="hljs-keyword">let</span> children =
      obj.children !== <span class="hljs-literal">undefined</span> ? (
        obj.children.map(<span class="hljs-function">(<span class="hljs-params">c, i</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> svgToJsx(c, index + <span class="hljs-string">"-"</span> + i);
        })
      ) : (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>
      );
    <span class="hljs-keyword">if</span> (obj.type === <span class="hljs-string">"text"</span>) {
      <span class="hljs-keyword">return</span> obj.value;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"tspan"</span>) {
      <span class="hljs-keyword">let</span> y = props.y ?? <span class="hljs-number">0</span> + props.dy ?? <span class="hljs-number">0</span>;
      <span class="hljs-keyword">let</span> x = props.x ?? <span class="hljs-number">0</span> + props.dx ?? <span class="hljs-number">0</span>;
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"tspan"</span>, children, y);
      <span class="hljs-keyword">return</span> children.length &gt; <span class="hljs-number">0</span> ? (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tspan</span> <span class="hljs-attr">x</span>=<span class="hljs-string">{x}</span> <span class="hljs-attr">y</span>=<span class="hljs-string">{y}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{props.key}</span>&gt;</span>
          {children}
        <span class="hljs-tag">&lt;/<span class="hljs-name">Tspan</span>&gt;</span></span>
      ) : (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span></span>
      );
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"text"</span>) {
      <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span>
          <span class="hljs-attr">x</span>=<span class="hljs-string">{props.x</span> ?? <span class="hljs-attr">0</span> + <span class="hljs-attr">props.dx</span> ?? <span class="hljs-attr">0</span>}
          <span class="hljs-attr">y</span>=<span class="hljs-string">{props.y</span> ?? <span class="hljs-attr">0</span> + <span class="hljs-attr">props.dy</span> ?? <span class="hljs-attr">0</span>}
          <span class="hljs-attr">key</span>=<span class="hljs-string">{props.key}</span>
        &gt;</span>
          {children}
        <span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>
      );
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"svg"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Svg</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Svg</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"path"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Path</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Path</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"line"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Line</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Line</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"polyline"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Polyline</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Polyline</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"polygon"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Polygon</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Polygon</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"rect"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Rect</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Rect</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"circle"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Circle</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Circle</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"ellipse"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Ellipse</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Ellipse</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"g"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">G</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">G</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"stop"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Stop</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Stop</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"defs"</span>) {
      <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
          {/*<span class="hljs-tag">&lt;<span class="hljs-name">Defs</span> {<span class="hljs-attr">...props</span>}&gt;</span>
        {obj.children !== undefined
          ? obj.children.map((c, i) =&gt; {
              return <span class="hljs-tag">&lt;&gt;</span><span class="hljs-tag">&lt;/&gt;</span>;// svgToJsx(c, index+"-"+i);
            })
          : undefined}
          <span class="hljs-tag">&lt;/<span class="hljs-name">Defs</span>&gt;</span>*/}
        <span class="hljs-tag">&lt;/&gt;</span></span>
      );
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"clipPath"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ClipPath</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">ClipPath</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"linearGradient"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LinearGradient</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">LinearGradient</span>&gt;</span></span>;
    }
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">"radialGradient"</span>) {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RadialGradient</span> {<span class="hljs-attr">...props</span>}&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">RadialGradient</span>&gt;</span></span>;
    }
  };

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
    {svgToJsx(parsedSVG.children[0], 0)}
  <span class="hljs-tag">&lt;/&gt;</span></span>
}
</code></pre>
<p>I hope you like this blog. If you have any questions please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><p><a target="_blank" href="https://react-pdf.org/svg">React PDF</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/diegomura/react-pdf/issues/1234">Github.com (Thank you @dennemark for creating the issue and sharing the idea)</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Installing Timescaledb on Postgres App(macOS)]]></title><description><![CDATA[Recently, while working on one of the projects. I came across the requirement to use Timescaledb. I use postgress.app for managing different versions of PostgreSQL for different projects. This was my first encounter with timescaledb, I had never used...]]></description><link>https://abulasar.com/installing-timescaledb-on-postgres-appmacos</link><guid isPermaLink="true">https://abulasar.com/installing-timescaledb-on-postgres-appmacos</guid><category><![CDATA[postgres.app]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[timescaledb]]></category><category><![CDATA[database]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Thu, 06 Oct 2022 12:56:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665060499497/tqxw2AwZv.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, while working on one of the projects. I came across the requirement to use Timescaledb. I use <a target="_blank" href="https://postgresapp.com/">postgress.app</a> for managing different versions of PostgreSQL for different projects. This was my first encounter with <code>timescaledb</code>, I had never used it before. So, I started to look at the official <a target="_blank" href="https://docs.timescale.com/install/latest/self-hosted/installation-macos/#install-self-hosted-timescaledb-using-homebrew">documentation</a> and follow the steps mentioned in the macOS (section). One thing, I was sure of after reading the documentation is that these steps will not be going to work in my case(with postgress.app). My suspicion was right and I struggled in configuring it. This mini blog is about the insight steps that I learned from different sources on the internet while installing timescaledb.</p>
<h3 id="heading-steps">Steps</h3>
<ul>
<li>The most obvious requirement for installing <code>timescaledb</code> is to have Postgress.app installed.</li>
<li>After installing Postgres.app we will install the timescaledb. For this, we will follow below steps from the official <a target="_blank" href="https://docs.timescale.com/install/latest/self-hosted/installation-macos/#install-self-hosted-timescaledb-using-homebrew">documentation</a>.</li>
<li>Open your terminal and run <ul>
<li><code>brew tap timescale/tap</code></li>
<li><code>brew install timescaledb</code></li>
</ul>
</li>
<li>Next, we have to run <code>timescaledb-tune</code> which requires passing the <code>Postgres</code> path with the version number of Postgres.(<strong>NOTE:</strong> Suppose, you have multiple version of Postgres say 13 and 14 and we want to install  timescaledb on version 14 so we will use <code>var-14</code> in the <code>conf-path</code>).</li>
<li>Run following script<pre><code>timescaledb-tune --yes --conf-path=<span class="hljs-regexp">/Users/</span><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">user_name</span>&gt;</span>/Library/Application\ Support/Postgres/var-14/postgresql.conf</span>
</code></pre></li>
<li>Navigate to <code>/opt/homebrew/Cellar/timescaledb/</code> and you will see folder with some number, it is nothing but timescaledb version. In my case, it is <code>2.8.1</code> so we will be using it in the below script. So, run the below script with your timescaledb number.</li>
</ul>
<pre><code>/usr/bin/install -c -m <span class="hljs-number">755</span> $(find /opt/homebrew/Cellar/timescaledb/<span class="hljs-number">2.8</span><span class="hljs-number">.1</span>/lib/timescaledb/postgresql@<span class="hljs-number">14</span>/ -name <span class="hljs-string">"timescaledb*.so"</span>)  /Applications/Postgres.app/Contents/Versions/<span class="hljs-number">14</span>/lib/postgresql

/usr/bin/install -c -m <span class="hljs-number">644</span> /opt/homebrew/Cellar/timescaledb/<span class="hljs-number">2.8</span><span class="hljs-number">.1</span>/share/timescaledb<span class="hljs-comment">/* /Applications/Postgres.app/Contents/Versions/14/share/postgresql/extension/</span>
</code></pre><ul>
<li>After this open the Postgress app and click the <code>Server Settings</code> button
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665059645555/EimdSMME-.png" alt="Screenshot 2022-10-06 at 6.04.00 PM.png" /></li>
<li>It will open a popup on which will click on the <code>show</code> button of <code>config</code> i.e second option.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665059721354/iprRfcDiR.png" alt="Screenshot 2022-10-06 at 6.05.15 PM.png" /></li>
<li>This will open a folder with <code>postgresql.conf</code> file selected. Now, open this file and add the following lines at the end of the file.<pre><code>shared_preload_libraries = <span class="hljs-string">'timescaledb'</span>
</code></pre></li>
<li>Now, restart the Postgres app. Double click on your database from the list of the database in the app. It will open the command prompt of the selected database. After that run the following command<pre><code>CREATE EXTENSION IF NOT EXISTS timescaledb;
</code></pre></li>
<li>This will add timescaledb for your particular database.</li>
</ul>
<p>I hope you like this blog. If you have any questions then please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://postgresapp.com/">Postgress.app</a></li>
<li><a target="_blank" href="https://www.timescale.com/">TimescaleDB</a></li>
<li><a target="_blank" href="https://docs.timescale.com/install/latest/self-hosted/installation-macos/#install-self-hosted-timescaledb-using-homebrew">Timescale Installation</a></li>
<li><a target="_blank" href="https://stackoverflow.com/a/73248120/8456101">Stackoverflow (Thank you Dr. Manhattan)</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Creating a real-time Trello board with Phoenix LiveView-Part-2]]></title><description><![CDATA[This blog is a continuation of a three-part blog series on creating a real-time Trello board. This is going to be part 2 of the series. In the previous blog, we designed and implemented the API layer ]]></description><link>https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-2</link><guid isPermaLink="true">https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-2</guid><category><![CDATA[Elixir]]></category><category><![CDATA[liveview]]></category><category><![CDATA[Real Time]]></category><category><![CDATA[Trello]]></category><category><![CDATA[javascript interop]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Thu, 08 Sep 2022 15:49:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Rua207zYcWg/upload/v1661920522515/XxjqSIVV_y.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog is a continuation of a three-part blog series on creating a real-time Trello board. This is going to be part 2 of the series. In the previous blog, we designed and implemented the API layer of the application. We added two tables i.e User and Task and also added the structs for the two tables. We've added and tested the API layer in the <code>iex</code> shell and it is working as per our expectations. In this blog, we are going to add the Trello board, Task cards, and integrate the functionalities with the previously designed API.</p>
<h3>Blog series!!!</h3>
<p>This is a 3 part blog series.</p>
<ul>
<li><p><a href="https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-1"><strong>Part 1</strong></a><strong>:</strong> We will be generating the application, adding the different tables, and the API layer, and we will test the API layer in the <code>iex</code> shell</p>
</li>
<li><p><strong>Part 2:</strong> We will build the board, add tasks UI, and integrate the board with the functions (API) we created in Part 1 (<strong>This is Part 2</strong>)</p>
</li>
<li><p><strong>Part 3:</strong> We will be adding real-time behavior to our board (under development)</p>
</li>
</ul>
<h3>Plan of attack</h3>
<ul>
<li><p>Getting the board and card UI ready ✓</p>
</li>
<li><p>Adding drag and drop feature to cards ✓</p>
</li>
<li><p>Adding JS hook and integrating with the UI✓</p>
</li>
<li><p>Integrating the UI with the API ✓</p>
</li>
</ul>
<h3>Getting the skeleton ready i.e <strong>Board</strong></h3>
<ul>
<li><p>As per our planning, in this part of the series, we have to create our Trello board with cards and drag-drop functionality.</p>
</li>
<li><p>We are going to add our dashboard in our <code>root</code> URL of the application.</p>
</li>
<li><p>For this, we will open <code>router.ex</code> and add the following code</p>
</li>
</ul>
<pre><code class="language-elixir">scope "/", TrelloAppWeb do
    pipe_through :browser

    live "/", HomeLive
end
</code></pre>
<ul>
<li><p>We have registered the root URL i.e <code>/</code> with the <code>home_live</code> file.</p>
</li>
<li><p>Now, create a file <code>trello_app/lib/trello_app_web/live/home_live.ex</code> and add following code</p>
</li>
</ul>
<pre><code class="language-elixir">  defmodule TrelloAppWeb.HomeLive do
    use TrelloAppWeb, :live_view
  
    def mount(_params, _session, socket) do
      { :ok, socket }
    end
  end
</code></pre>
<ul>
<li><p>Before creating any view of the LiveView we will add some styling for the board and the cards.</p>
</li>
<li><p>Open the <code>app.css</code> file in the <code>assets</code> folder and paste the following CSS stylings.</p>
</li>
</ul>
<pre><code class="language-elixir">  html {
    font-family: sans-serif;
    background: #b1dade;
    color: #4a6064;
    font-weight: 400;
  }
  
  body {
    margin: 0;
  }
  
  * {
    box-sizing: border-box;
  }
  
  h1, h2, h3, h4, h5, h6 {
    font-weight: 400;
  }
  
  h1 {
    margin: 0;
    background: #303945;
    color: #fff;
    font-weight: 400;
    padding: 0.5rem;
    font-size: 1.2rem;
  }
  
  main {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
  }
  
  .fullwidth {
    width: 100%;
  }
  
  .board {
    flex-grow: 1;
    width: 100%;
    padding: 1rem;
    overflow: scroll;
    display: flex;
    align-items: flex-start;
  }
  
  .list {
    margin: 0;
    min-width: 14rem;
    flex-basis: calc(100% / 6 - 1rem);
    padding: 0;
    list-style: none;
    margin: 0.5rem;
    background: #e7f3f4;
    color: #4a6064;
    border-radius: 0.2rem;
    overflow: hidden;
    padding-bottom: 8px;
  }
  
  .list-form {
    margin: 0;
    min-width: 14rem;
    flex-basis: calc(100% / 6 - 1rem);
    padding: 0;
    list-style: none;
    margin: 0.5rem;
    background: #e7f3f4;
    color: #4a6064;
    border-radius: 0.2rem;
    overflow: hidden;
  }
  
  .list-form form {
    padding: 0.5rem;
  }
  
  .list-form form &gt; * + * {
    margin-top: 0.5rem;
  }
  
  .list-form h2,
  .list-header {
    font-size: 0.8rem;
    font-weight: 600;
    margin: 0;
    padding: 0.5rem;
    padding-bottom: 0;
  }
  
  .list-footer {
    margin-top: 0.5rem;
    padding: 0.5rem;
    overflow: auto;
    background: #ddeff0;
    color: #4a6064;
  }
  
  .card {
    margin: 0.5rem;
    margin-bottom: 0;
    background: #fff;
    color: #4a6064;
    padding: 0.5rem;
    border-radius: 0.2rem;
    height: 100px;
  }
  
  .card:first-of-type {
    margin: 0;
  }
  
  .card a {
    color: inherit;
    text-decoration: none;
  }
</code></pre>
<ul>
<li><p>Now, add a file <code>home_live.html.heex</code> in the same folder(of <code>home_live.ex</code>) and add <code>h1</code> tag with some random message for testing purposes.</p>
</li>
<li><p>As per our design we want three columns <code>planning</code>, <code>progress</code>, and <code>completed</code> to display our cards. Refer to the below image to get an idea.</p>
  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661375604438/eFulSAFQG.png" alt="grouped_tasks (1).png" style="display:block;margin:0 auto" />
  </li>
<li><p>Now, in the <code>home_live.html.heex</code> add these three columns with <code>id</code> as per the status.</p>
</li>
</ul>
<pre><code class="language-elixir">&lt;div class="board" id="trello-board"&gt;
    &lt;ul class="list" id="planning"&gt;
        &lt;div class="list-header"&gt; Planning &lt;/div&gt;
    &lt;/ul&gt;

    &lt;ul class="list" id="progress"&gt;
       &lt;div class="list-header"&gt; In Progress &lt;/div&gt;
    &lt;/ul&gt;

    &lt;ul class="list" id="completed"&gt;
       &lt;div class="list-header"&gt; In Progress &lt;/div&gt;
    &lt;/ul&gt;
&lt;/div&gt;
</code></pre>
<ul>
<li>It will just create empty columns without any card because we haven't added any.</li>
</ul>
<h3>Getting the Card ready</h3>
<ul>
<li><p>If you remember we have created a function <code>get_grouped_tasks/0</code> in the <code>Project</code> context.</p>
</li>
<li><p>We will assign the value of this function to the assigns in the <code>mount</code> function and later will iterate over these values in the view.</p>
</li>
<li><p>In the <code>mount</code> function of the <code>home_live.ex</code>, add the following code.</p>
</li>
</ul>
<pre><code class="language-elixir">  defmodule TrelloAppWeb.HomeLive do
    ....
    alias TrelloApp.Organization
  
    def mount(_params, _session, socket) do
      tasks = Organization.get_grouped_tasks()
      {:ok, assign(socket, tasks: tasks)}
    end  
  end
</code></pre>
<ul>
<li><p>We added an <code>alias</code> of the <code>Organization</code> context and assigned <code>tasks</code> to the socket using the <code>get_grouped_tasks</code> function.</p>
</li>
<li><p>If you remember <code>get_grouped_tasks</code> returns key-value pair of task <code>status</code> as key and a list of <code>tasks</code> as value.</p>
</li>
<li><p>Something like this</p>
</li>
</ul>
<pre><code class="language-elixir">%{
  "planning" =&gt; [
      %TrelloApp.Organization.Task{
        __meta__: #Ecto.Schema.Metadata&lt;:loaded, "tasks"&gt;,
        description: "designing api",
        id: 2,
        inserted_at: ~N[2022-08-25 09:30:08],
        state: "planning",
        title: "Designing API",
        updated_at: ~N[2022-08-25 09:30:08],
        user: %TrelloApp.Organization.User{
          __meta__: #Ecto.Schema.Metadata&lt;:loaded, "users"&gt;,
          first_name: "Michael",
          id: 1,
          inserted_at: ~N[2022-08-25 08:45:25],
          last_name: "holding",
          tasks: #Ecto.Association.NotLoaded&lt;association :tasks is not loaded&gt;,
          updated_at: ~N[2022-08-25 08:45:25]
        },
        user_id: 1
      }
  ],
  "progress" =&gt; [
      // two tasks under progress
      %TrelloApp.Organization.Task{ id: 2 },  
      %TrelloApp.Organization.Task{ id: 3 }
   ],
   "completed" =&gt; [
      // one task under completed
      %TrelloApp.Organization.Task{id: 4}
   ]
}
</code></pre>
<ul>
<li>Now, we will iterate over each of the lists in each column.</li>
</ul>
<pre><code class="language-elixir">  &lt;div class="board" id="trello-board"&gt;
      &lt;ul class="list" id="planning"&gt;
          &lt;div class="list-header"&gt; Planning &lt;/div&gt;
          &lt;%= for task &lt;- @tasks["planning"] || [] do %&gt;
             &lt;li class="card"&gt;
                &lt;a href&gt;&lt;%= task.title %&gt;&lt;/a&gt;
                 &lt;i&gt;&lt;%= full_name({title.user}) %&gt;&lt;/i&gt;
              &lt;/li&gt;
          &lt;% end %&gt;
      &lt;/ul&gt;
  
      &lt;ul class="list" id="progress"&gt;
         &lt;div class="list-header"&gt; In Progress &lt;/div&gt;
         &lt;%= for task &lt;- @tasks["progress"] || [] do %&gt;
             &lt;li class="card"&gt;
                &lt;a href&gt;&lt;%= task.title %&gt;&lt;/a&gt;
                 &lt;i&gt;&lt;%= full_name({title.user}) %&gt;&lt;/i&gt;
              &lt;/li&gt;
          &lt;% end %&gt;
      &lt;/ul&gt;
  
      &lt;ul class="list" id="completed"&gt;
         &lt;div class="list-header"&gt; In Progress &lt;/div&gt;
         &lt;%= for task &lt;- @tasks["completed"] || [] do %&gt;
             &lt;li class="card"&gt;
                &lt;a href&gt;&lt;%= task.title %&gt;&lt;/a&gt;
                 &lt;i&gt;&lt;%= full_name({title.user}) %&gt;&lt;/i&gt;
              &lt;/li&gt;
          &lt;% end %&gt;
      &lt;/ul&gt;
  &lt;/div&gt;
</code></pre>
<ul>
<li>This will render all cards in each column. Now, we will work on adding the <code>drag and drop</code> feature to our board.</li>
</ul>
<h3>Adding drag and drop feature to cards</h3>
<p>I wanted a vanilla javascript library that should be lightweight and highly supportive in the js community. While doing some research I came across <a href="https://github.com/SortableJS/Sortable">sortable-js</a>. Let's install it in the application. Navigate to the <code>assets</code> directory by running <code>cd assets</code> then run</p>
<pre><code class="language-elixir">  npm install sortablejs --save
</code></pre>
<ul>
<li><p>I went through <code>sortablejs</code> documentation and came across a <code>create</code> method which can help us to move the cards from one point to another.</p>
</li>
<li><p>It takes the element reference of the column that you want to be sortable as the first argument and the second argument will be an object with different properties you want to apply(as per documentation).</p>
</li>
</ul>
<pre><code class="language-elixir">    Sortable.create(planning, {
        group: 'shared',
        animation: 150,
        sort: false,
        onEnd: function (/**Event*/evt) {}
    });
</code></pre>
<ul>
<li>The last property in the object i.e <code>onEnd</code> callback is where we have to do all manipulation. It is going to execute as soon as we drop the card on the other column. It has one event parameter which will have all information like the detail of the dragged element and the column where it is dropped.</li>
</ul>
<h3>Adding meta-data to each card</h3>
<ul>
<li><p>We've discussed the <code>onEnd</code> callback in the <code>create</code> method. It has some information about the dragged element and the point where it is dropped.</p>
</li>
<li><p>We are going to add a <code>data</code> property with the name <code>task-id</code> and value as <code>id</code> of the task on each card. Notice, the <code>data-task-id</code> property in the <code>li</code> tag.</p>
</li>
</ul>
<pre><code class="language-elixir">    &lt;%= for task &lt;- @tasks["completed"] || [] do %&gt;
       &lt;li class="card" data-task-id={"#{task.id}"}&gt; &lt;==Notice this
          &lt;a href&gt;&lt;%= task.title %&gt;&lt;/a&gt;
           &lt;i&gt;&lt;%= full_name({title.user}) %&gt;&lt;/i&gt;
        &lt;/li&gt;
      &lt;% end %&gt;
</code></pre>
<ul>
<li><p>This <code>data-task-id</code> will act as the information of the element that is being dragged.</p>
</li>
<li><p>Also, if you remember the <code>id</code> on each column i.e <code>ul</code> tag, will act as information where the <code>card</code> is being dropped.</p>
</li>
</ul>
<pre><code class="language-elixir">  &lt;ul class="list" id="completed"&gt;
</code></pre>
<ul>
<li>So, in <code>onEnd</code> callback we can access <code>id</code> and <code>taskId</code> as follows</li>
</ul>
<pre><code class="language-elixir">    console.log(`Id: ${evt.to.id}`) 
    console.log(`taskId: ${evt.item.dataset.taskId}`)
</code></pre>
<ul>
<li>We will use this information when we are going to write our javascript hook to make all things work.</li>
</ul>
<h3>Adding Javascript Hook</h3>
<p>We have installed <code>sortablejs</code> and discussed the method and made some changes in the Html to make it work with our javascript logic. Now, we will add the javascript logic. We will add a <code>mounted</code> callback, which is called and initialized with some initial value as soon as the element on which we are applying it is mounted in the DOM.</p>
<ul>
<li><p>Let's add a file in <code>trello_board.js</code> in <code>assets/js</code> folder.</p>
</li>
<li><p>We will initialize <code>Sortable.create</code> for all of the three columns. We will get all of the three columns using <code>document.getElementById('column_id')</code>. Refer to the code below.</p>
</li>
</ul>
<pre><code class="language-elixir">  import Sortable from 'sortablejs';

  TrelloBoard = {
    mounted(){ 
      const planning = document.getElementById('planning')
      const progress = document.getElementById('progress')
      const completed = document.getElementById('completed')
      const pushEvent = (target, taskId) =&gt; {
        this.pushEvent("move_task", {target, taskId})
      }

      Sortable.create(planning, {
          group: 'shared',
          animation: 150,
          sort: false,
          onEnd: function (/**Event*/evt) {
            pushEvent(evt.to.id, evt.item.dataset.taskId) 
          }
      });

      Sortable.create(progress, {
          group: 'shared',
          animation: 150,
          sort: false,
          onEnd: function (/**Event*/evt) {
            pushEvent(evt.to.id, evt.item.dataset.taskId) 
          }
      });

      Sortable.create(completed, {
          group: 'shared',
          animation: 150,
          sort: false,
          onEnd: function (/**Event*/evt) {
            pushEvent(evt.to.id, evt.item.dataset.taskId) 
          }
      });
  }
 export default TrelloBoard
</code></pre>
<ul>
<li><p>Everything seems similar to what we discussed earlier, except the <code>pushEvent</code> function.</p>
</li>
<li><p>This function is initialized on the fourth line of the <code>mounted</code> function and it takes <code>target</code> and <code>taskId</code> as the parameter and creates an event "move_task"<code>using</code>this.pushEvent` function and passes these two parameters as an object.</p>
</li>
<li><p>At last but not least we will include our <code>TrelloBoard</code> hook in our LiveSocket. For this, we will open the <code>app.js</code> file and import <code>TrelloBoard</code> and include it in our Hooks object, and add the Hooks to our LiveSocket constructor as follows.</p>
</li>
</ul>
<pre><code class="language-elixir">    import TrelloBoard from "./trello_board"
    let Hooks = { TrelloBoard }

    let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks }) 
    # Notice `hooks: Hooks`
</code></pre>
<h3>Calling the Hook</h3>
<p>To call our Javascript logic i.e hook we’ll add the <code>phx-hook</code> attribute on the Trello board with value as the hook name i.e <code>TrelloBoard</code> where we want to perform our drag-drop operations.</p>
<pre><code class="language-elixir">&lt;div class="board" id="trello-board" phx-hook="TrelloBoard"&gt;
  &lt;ul class="list" id="planning"&gt;
    &lt;div class="list-header"&gt; Planning &lt;/div&gt;
      .........
      .........
      .........
   // Remaining UI code
&lt;/div&gt;
</code></pre>
<h3>Handling <code>move_task</code> event in LiveView</h3>
<ul>
<li><p>In our javascript hooks <code>onEnd</code> callback we created a <code>pushEvent</code> named <code>move_task</code> which also sends <code>target</code> and <code>taskId</code>.</p>
</li>
<li><p>In LiveView, the events which are sent by the client using <code>pushEvents</code> can be handled using <code>handle_event</code>.</p>
</li>
<li><p>Open the <code>home_live.ex</code> file and add the following function.</p>
</li>
</ul>
<pre><code class="language-elixir">  defmodule TrelloAppWeb.HomeLive do
     .........
     .........
     .........
  
    def handle_event("move_task", %{"target" =&gt; target, "taskId" =&gt; taskId}, socket) do
        Project.change_task_state(taskId, target)
        {:noreply, socket}
    end
  end
</code></pre>
<ul>
<li><p>As you can see in this <code>handle_event</code> function we are extracting <code>target</code> and <code>taskId</code> using pattern matching.</p>
</li>
<li><p>Later, we used the <code>change_task_state/2</code> function(which we created in Part 1 of this blog series) and passed the <code>taskId</code> and <code>target</code> parameters to the function.</p>
</li>
<li><p>This not only moves the card in UI but also persists the state in the database.</p>
</li>
</ul>
<p>This completes the second blog of this series. In this part, we build the board, add tasks UI along with drag and drop functionality and integrated the board with our API layer. In the next part, we will make our Trello board in real-time. I hope you like this blog. If you have any questions then please comment below. Thanks for reading 😊.</p>
<h3>References</h3>
<ul>
<li><p><a href="https://github.com/abulsayyad123/trello-board-liveview">Github Repo</a></p>
</li>
<li><p><a href="https://sortablejs.github.io/Sortable/">Sortablejs</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Creating a real-time Trello board with Phoenix LiveView-Part-1]]></title><description><![CDATA[Trello cards help you to collaborate, manage projects, and reach new productivity peaks(As per their official Website 😊). You may have used it earlier or some similar board like Jira to manage projects and tasks. When I joined one of my previous org...]]></description><link>https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-1</link><guid isPermaLink="true">https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-1</guid><category><![CDATA[liveview]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[Trello]]></category><category><![CDATA[Real Time]]></category><category><![CDATA[javascript interop]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Thu, 08 Sep 2022 15:44:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/Rua207zYcWg/upload/v1661920441891/Fi5vQ1NtX.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[
<p><a target="_blank" href="https://trello.com/home">Trello</a> cards help you to collaborate, manage projects, and reach new productivity peaks(As per their official Website 😊). You may have used it earlier or some similar board like <a target="_blank" href="https://www.atlassian.com/software/jira">Jira</a> to manage projects and tasks. When I joined one of my previous organizations, my training was tracked using the Trello board which helped me and other new joiners in learning and track our progress. In this Blog, we are about to create a similar board with a real-time update feature. </p>
<h3 id="heading-what-are-going-to-build">What are going to build?</h3>
<p>We are going to build a Trello look-alike board. It will have different cards belonging to different sections. We can drag and drop cards to different sections with a smooth transition. Along with the drag-and-drop transition of cards to different sections, the whole experience will be real-time. If any user makes any change in the card section then any other user watching the application screen doesn't have to explicitly refresh the browser to see the effect. Refer to the <code>gif</code> below to get an idea.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660508087985/GazzESvF0.gif" alt="tasker.gif" class="image--center mx-auto" /></p>
<h3 id="heading-blog-series">Blog series!!!</h3>
<p>This is a 3 part blog series. </p>
<ul>
<li><strong>Part 1:</strong> We will be generating the application, adding the different tables, and the API layer, and we will test the API layer in the <code>iex</code> shell (This is Part 1)</li>
<li><strong><a target="_blank" href="https://abulasar.com/creating-a-real-time-trello-board-with-phoenix-liveview-part-2">Part 2</a>:</strong> We will build the board, add tasks UI, and integrate the board with the functions (API) we created in Part 1</li>
<li><strong>Part 3:</strong> We will be adding real-time behavior to our board (under development)</li>
</ul>
<h3 id="heading-action-plan">Action Plan</h3>
<ul>
<li>Creating a LiveView project</li>
<li>Adding User and Task Tables.</li>
<li>Adding Task and User struct</li>
<li>Designing the API Layer</li>
</ul>
<p>Let's start building 👷</p>
<h3 id="heading-creating-liveview-project">Creating LiveView project</h3>
<ul>
<li>First of all, our system should have Elixir installed. We can confirm this by running <code>Elixir -v</code>.</li>
<li>Once confirmed we need to create/generate the Phoenix LiveView application.</li>
<li>This can be done by running <code>mix phx.new trello_app  --live</code></li>
<li>if you get this error <pre><code>** (Mix) The task <span class="hljs-string">"phx.new"</span> could not be found
Note no mix.exs was found <span class="hljs-keyword">in</span> the current directory
</code></pre>Then run  <code>mix archive.install hex phx_new</code></li>
<li>Then navigate to project directory <code>cd trello_app</code> and run <code>mix phx.server</code>. Once, the project starts running navigate to localhost:4000 to see the welcome phoenix screen.</li>
</ul>
<h3 id="heading-adding-user-and-task-tables">Adding User and Task Tables.</h3>
<ul>
<li>As per our requirement, we have two entities that need to be created, the <code>card</code> and the <code>user</code> to which this card belongs. We will call these cards <code>Task</code>.</li>
<li>So, we will add two tables the <code>User</code> table and the <code>Task</code> table. </li>
<li>Let's add migration files for these two Tables.</li>
<li>For the sake of clarity we will add two separate migration files for these two tables.</li>
<li>For <code>User</code> table run <code>mix ecto.gen.migration create_user</code> and for <code>Task</code> table run <code>mix ecto.gen.migration create_task</code></li>
<li><p>Now open <code>trello_app/priv/repo/migrations/{{some_time_stamp}}_create_user.exs</code> migration file at and the add following fields in the migration file.</p>
<pre><code>defmodule TrelloApp.Repo.Migrations.CreateUser <span class="hljs-keyword">do</span>
  use Ecto.Migration

  def change <span class="hljs-keyword">do</span>
     create table(:users) <span class="hljs-keyword">do</span>
        <span class="hljs-attr">add</span> :first_name, :string, <span class="hljs-attr">null</span>: <span class="hljs-literal">false</span>
        <span class="hljs-attr">add</span> :last_name, :string, <span class="hljs-attr">null</span>: <span class="hljs-literal">false</span>

        timestamps()
     end
  end
end
</code></pre></li>
<li><p>Similarly for <code>task</code> table open <code>trello_app/priv/repo/migrations/{{some_time_stamp}}_create_task.exs</code> migration file and the following fields</p>
<pre><code>defmodule TrelloApp.Repo.Migrations.CreateTask <span class="hljs-keyword">do</span>
   use Ecto.Migration

   def change <span class="hljs-keyword">do</span>
      create table(:tasks) <span class="hljs-keyword">do</span>
         <span class="hljs-attr">add</span> :title, :string, <span class="hljs-attr">null</span>: <span class="hljs-literal">false</span>
         <span class="hljs-attr">add</span> :description, :text
         <span class="hljs-attr">add</span> :user_id, references(:users)
         <span class="hljs-attr">add</span> :state, :string, <span class="hljs-attr">null</span>: <span class="hljs-literal">false</span>

         timestamps()
     end
 end
end
</code></pre></li>
<li>Now we will now run the command <code>mix ecto.migrate</code>. This will create tables <code>User</code> and <code>Task</code> in the database.</li>
</ul>
<h3 id="heading-adding-task-and-user-struct">Adding Task and User struct</h3>
<ul>
<li>We have created <code>User</code> and <code>Task</code> tables now we are going to add <code>User</code> and <code>Task</code> structs so that we can use <code>Ecto</code> queries effectively.</li>
<li>First, we will add the <code>Task</code> struct. For that we will create a file <code>trello_app/lib/trello_app/organization/task.ex</code>.</li>
<li><p>Add the following code to the file.</p>
<pre><code>defmodule TrelloApp.Organization.Task <span class="hljs-keyword">do</span>
  use Ecto.Schema
  <span class="hljs-keyword">import</span> Ecto.Changeset

  alias TrelloApp.Organization.{ Task, User }

  schema <span class="hljs-string">"tasks"</span> <span class="hljs-keyword">do</span>
     <span class="hljs-attr">field</span> :title, :string
     <span class="hljs-attr">field</span> :description, :string
     <span class="hljs-attr">field</span> :state, :string

     <span class="hljs-attr">belongs_to</span> :user, User

     timestamps()
  end

  def changeset(%Task{} = task, attrs) <span class="hljs-keyword">do</span>
    task
    |&gt; cast(attrs, [:title, :description, :state, :user_id])
  end
end
</code></pre></li>
<li>Similarly, we will add the <code>User</code> struct in the same organization directory with a file name <code>user.ex</code>.</li>
<li><p>We will add a similar code here as well.</p>
<pre><code>defmodule TrelloApp.Organization.User <span class="hljs-keyword">do</span>
  use Ecto.Schema
  <span class="hljs-keyword">import</span> Ecto.Changeset

  alias TrelloApp.Organization.{ Task, User }
  schema <span class="hljs-string">"users"</span> <span class="hljs-keyword">do</span>
    <span class="hljs-attr">field</span> :first_name
    <span class="hljs-attr">field</span> :last_name

    <span class="hljs-attr">has_many</span> :task, Task
    timestamps()
  end

  def changeset(%User{} = user, attrs) <span class="hljs-keyword">do</span>
     user
     |&gt; cast(attrs, [:first_name, :last_name])
  end
end
</code></pre></li>
<li>In the above code, both the structs i.e <code>User</code> and <code>Task</code> has fields defined in the <code>schema</code> macro similar to the migration files. </li>
<li>We've also defined the relationship between the structs(Tables). It is a <code>one-to-many</code> relationship between <code>User</code> and <code>Task</code> which means <code>User</code> can have many <code>Task</code>. We've also defined the <code>changeset</code> functions for manipulating the data.</li>
<li>Now, to test if everything is working fine we will add some dummy data in both the tables using the <code>iex</code> shell.</li>
<li>Open the terminal and navigate to the root directory level of the project. Now run the <code>iex</code> shell by typing <code>iex -S mix</code>.</li>
<li>Now alias both the structs and <code>Repo</code> to avoid typing long struct names.<pre><code>alias TrelloApp.Organization.{ Task, User }
</code></pre></li>
<li><p>First, we will insert the <code>User</code> record for that we will run the following code.</p>
<pre><code>User.changeset(%User{}, %{<span class="hljs-attr">first_name</span>: <span class="hljs-string">"Michael"</span>, <span class="hljs-attr">last_name</span>: <span class="hljs-string">"Jordan"</span>}) 
|&gt; Repo.insert

User.changeset(%User{}, %{<span class="hljs-attr">first_name</span>: <span class="hljs-string">"Andrew"</span>, <span class="hljs-attr">last_name</span>: <span class="hljs-string">"Flintoff"</span>}) 
|&gt; Repo.insert
</code></pre></li>
<li>This will insert two <code>User's</code> records with <code>id</code> of 1 and 2.</li>
<li><p>Similarly, we will add a few <code>Task</code> records for both <code>users</code> but with different states. For testing purposes, we will add four tasks with the states "planning", "progress", and "completed".</p>
<pre><code>%Task{} 
|&gt; Task.changeset(%{<span class="hljs-attr">title</span>: <span class="hljs-string">"Designing API"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"designing api"</span>, <span class="hljs-attr">state</span>: <span class="hljs-string">"planning"</span>, <span class="hljs-attr">user_id</span>: <span class="hljs-number">1</span>}) 
|&gt; Repo.insert

%Task{} 
|&gt; Task.changeset(%{<span class="hljs-attr">title</span>: <span class="hljs-string">"Take Backup"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"take backup"</span>, <span class="hljs-attr">state</span>: <span class="hljs-string">"progress"</span>, <span class="hljs-attr">user_id</span>: <span class="hljs-number">2</span>}) 
|&gt; Repo.insert

%Task{} 
|&gt; Task.changeset(%{<span class="hljs-attr">title</span>: <span class="hljs-string">"Add Migrations"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"write migrations"</span>, <span class="hljs-attr">state</span>: <span class="hljs-string">"progress"</span>, <span class="hljs-attr">user_id</span>: <span class="hljs-number">1</span>}) 
|&gt; Repo.insert

%Task{} 
|&gt; Task.changeset(%{<span class="hljs-attr">title</span>: <span class="hljs-string">"Write Script"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"write script"</span>, <span class="hljs-attr">state</span>: <span class="hljs-string">"completed"</span>, <span class="hljs-attr">user_id</span>: <span class="hljs-number">2</span>}) 
|&gt; Repo.insert
</code></pre></li>
<li>After running the above queries we will have one <code>User</code> record and three <code>Task</code> records.</li>
</ul>
<h3 id="heading-designing-the-api-layer">Designing the API Layer</h3>
<ul>
<li>As per our requirement we only want two functions in our API Layer, which are <code>get_grouped_tasks</code> and <code>change_task_state</code>.</li>
<li><h4 id="heading-what-getgroupedtasks-will-do">What <code>get_grouped_tasks</code> will do?</h4>
</li>
<li>If you remember in the <code>Task</code> struct we have a <code>state</code> field. We've added three <code>tasks</code> records with state <code>planning</code>, <code>progress</code>, and <code>completed</code>.</li>
<li>We want our tasks should be grouped (as per the <code>state</code> mentioned in the task) and placed in the respective <code>states</code> column.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661375604438/eFulSAFQG.png" alt="grouped_tasks (1).png" class="image--center mx-auto" /></li>
<li>In this function, we will have three stages of data transformation<ul>
<li><em>Fetch all tasks</em></li>
<li><em>Preload the owner of the tasks (Just for beautification of the task to mention the owner of the task)</em></li>
<li><em>Grouping the tasks</em></li>
</ul>
</li>
<li>Below is the code of the above pseudo-code.<pre><code>def get_grouped_tasks() <span class="hljs-keyword">do</span>
  Task
  |&gt; Repo.all()
  |&gt; Repo.preload(:user)
  |&gt; Enum.group_by(fn %{<span class="hljs-attr">state</span>: state} -&gt; state end)
end
</code></pre></li>
<li>Our second method will be <code>change_task_state</code>.</li>
<li><h4 id="heading-what-changetaskstate-will-do">What <code>change_task_state</code> will do?</h4>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661375619056/7CsOQK3Ex.png" alt="add_task (1).png" class="image--center mx-auto" /></li>
<li>When we move our task (say) from <code>Progress</code> to <code>Completed</code>, we want to change the <code>state</code> field of the task from <code>progress</code> to <code>completed</code>.</li>
<li>This function will take two parameters <code>task_id</code> and <code>transition_state</code> i.e the <code>column</code> we want our task should be moved.</li>
<li>In this function, we will have three stages of data transformation<ul>
<li><em>Get <code>task</code> by <code>task_id</code> parameter</em></li>
<li><em>Change the <code>state</code> of the <code>task</code> as per provided <code>transition_state</code> using <code>changeset</code> function in <code>Task</code> struct</em></li>
<li><em>Update the <code>Task</code></em></li>
</ul>
</li>
<li>Below is the code of the above pseudo-code.<pre><code>def change_task_state(task_id, transition_state) <span class="hljs-keyword">do</span>
  Task
  |&gt; Repo.get(task_id)
  |&gt; Task.changeset(%{<span class="hljs-attr">state</span>: transition_state})
  |&gt; Repo.update
end
</code></pre></li>
<li>We are going to place both functions in a separate module <code>organization.ex</code> under <code>trello_app/lib/trello_app</code> folder.</li>
<li><p>This module will be the provider of the two functions, which we will use in the LiveView module later.</p>
<pre><code>defmodule TrelloApp.Organization <span class="hljs-keyword">do</span>
  alias TrelloApp.Repo
  alias TrelloApp.Organization.Task

  def get_grouped_tasks() <span class="hljs-keyword">do</span>
    Task
    |&gt; Repo.all()
    |&gt; Repo.preload(:user)
    |&gt; Enum.group_by(fn %{<span class="hljs-attr">state</span>: state} -&gt; state end)
  end

  def change_task_state(task_id, transition_state) <span class="hljs-keyword">do</span>
    Task
    |&gt; Repo.get(task_id)
    |&gt; Task.changeset(%{<span class="hljs-attr">state</span>: transition_state})
    |&gt; Repo.update
  end
end
</code></pre></li>
</ul>
<h3 id="heading-testing-the-api-layer-in-the-iex-shell">Testing the API Layer in the <code>iex</code> shell</h3>
<ul>
<li>We've created our Task and User structs as well as designed our API Layer. Let's test the API Layer in the iex shell.</li>
<li>If you remember, we've added a few records of <code>task</code> and <code>user</code>.</li>
<li>So, as per our API layer, we have to functions <code>get_grouped_tasks/0</code> and <code>change_task_state/2</code>. First, we will test <code>get_grouped_tasks/0</code>,</li>
<li>For testing, we will alias the <code>Organization</code> module <pre><code>  alias TrelloApp.Organization
</code></pre></li>
<li>Now run <code>Organization.get_grouped_tasks()</code>, this will return grouped tasks as per their <code>states</code><pre><code>%{
<span class="hljs-string">"planning"</span> =&gt; [
    %TrelloApp.Organization.Task{
      <span class="hljs-attr">__meta__</span>: #Ecto.Schema.Metadata&lt;:loaded, <span class="hljs-string">"tasks"</span>&gt;,
      description: <span class="hljs-string">"designing api"</span>,
      <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
      <span class="hljs-attr">inserted_at</span>: ~N[<span class="hljs-number">2022</span><span class="hljs-number">-08</span><span class="hljs-number">-25</span> <span class="hljs-number">09</span>:<span class="hljs-number">30</span>:<span class="hljs-number">08</span>],
      <span class="hljs-attr">state</span>: <span class="hljs-string">"planning"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Designing API"</span>,
      <span class="hljs-attr">updated_at</span>: ~N[<span class="hljs-number">2022</span><span class="hljs-number">-08</span><span class="hljs-number">-25</span> <span class="hljs-number">09</span>:<span class="hljs-number">30</span>:<span class="hljs-number">08</span>],
      <span class="hljs-attr">user</span>: %TrelloApp.Organization.User{
        <span class="hljs-attr">__meta__</span>: #Ecto.Schema.Metadata&lt;:loaded, <span class="hljs-string">"users"</span>&gt;,
        first_name: <span class="hljs-string">"Michael"</span>,
        <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">inserted_at</span>: ~N[<span class="hljs-number">2022</span><span class="hljs-number">-08</span><span class="hljs-number">-25</span> <span class="hljs-number">08</span>:<span class="hljs-number">45</span>:<span class="hljs-number">25</span>],
        <span class="hljs-attr">last_name</span>: <span class="hljs-string">"holding"</span>,
        <span class="hljs-attr">tasks</span>: #Ecto.Association.NotLoaded&lt;association :tasks is not loaded&gt;,
        updated_at: ~N[<span class="hljs-number">2022</span><span class="hljs-number">-08</span><span class="hljs-number">-25</span> <span class="hljs-number">08</span>:<span class="hljs-number">45</span>:<span class="hljs-number">25</span>]
      },
      <span class="hljs-attr">user_id</span>: <span class="hljs-number">1</span>
    }
],
<span class="hljs-string">"progress"</span> =&gt; [
    <span class="hljs-comment">// two tasks under progress</span>
    %TrelloApp.Organization.Task{ <span class="hljs-attr">id</span>: <span class="hljs-number">2</span> },  
    %TrelloApp.Organization.Task{ <span class="hljs-attr">id</span>: <span class="hljs-number">3</span> }
 ],
 <span class="hljs-string">"completed"</span> =&gt; [
    <span class="hljs-comment">// one task under completed</span>
    %TrelloApp.Organization.Task{<span class="hljs-attr">id</span>: <span class="hljs-number">4</span>}
 ]
}
</code></pre></li>
<li>We got one task for <code>planning</code>, one task for <code>completed</code>, and two for <code>progress</code>. This is exactly as per the <code>tasks</code> we added with different states.</li>
<li>So far so good, now we will test <code>change_task_state</code> for first <code>task</code> i.e id=1.</li>
<li><code>change_task_state/2</code> expects two parameters <code>task_id</code> and <code>transtion_state</code>.</li>
<li>We will move our task with <code>id=1</code> to the <code>completed</code> state. So, we will run <code>Organization.change_task_state(1, 'completed')</code> in our shell.</li>
<li>This will transition our task of <code>id=1</code> to the <code>completed</code> column. We can test this by again running the <code>get_grouped_task</code>. It will not return the <code>planning</code> group since there is no task left with the <code>planning</code> state.</li>
</ul>
<p>This completes the first blog of this series. In the next part as per our planning, we will build the board, add tasks UI along with drag and drop functionality and will integrate the board with our API layer. I hope you like this blog. If you have any questions then please comment below. Thanks for reading 😊. </p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://github.com/abulsayyad123/trello-board-liveview">Github Repo</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[My Elixir Mix Podcast]]></title><description><![CDATA[Recently, I got the opportunity to be a guest on Elixir Mix podcast. Though I was nervous(super nervous) but the hosts of the show Sascha Wolf, Adi Iyengar, and Allen Wyma were very welcoming and made me comfortable.
The podcast was about my blog Fet...]]></description><link>https://abulasar.com/my-elixir-mix-podcast</link><guid isPermaLink="true">https://abulasar.com/my-elixir-mix-podcast</guid><category><![CDATA[Elixir]]></category><category><![CDATA[liveview]]></category><category><![CDATA[podcast]]></category><category><![CDATA[Phoenix framework]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sat, 23 Jul 2022 14:15:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/h6PDEdr9IZo/upload/v1658584657064/ZxXjHAMh_.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I got the opportunity to be a guest on <a target="_blank" href="https://topenddevs.com/podcasts/elixir-mix/">Elixir Mix</a> podcast. Though I was nervous(super nervous) but the hosts of the show Sascha Wolf, Adi Iyengar, and Allen Wyma were very welcoming and made me comfortable.</p>
<p>The podcast was about my blog <strong><a target="_blank" href="https://abulasar.com/fetching-data-from-external-graphql-api-service-in-phoenix-liveview">Fetching data from external Graphql API service in Phoenix LiveView</a></strong>.
We discussed Elixir, LiveView, Graphql, and its testing. I really enjoyed being on the podcast and discussing Elixir with the team. </p>
<p><a target="_blank" href="https://topenddevs.com/podcasts/elixir-mix/episodes/combining-graphql-and-liveview-with-abul-asar-sayyad-emx-182">This</a> is the link to the podcast if you are interested in listening and I hope you'll like the podcast.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://topenddevs.com/podcasts/elixir-mix/episodes/combining-graphql-and-liveview-with-abul-asar-sayyad-emx-182">My Podcast</a></li>
<li><a target="_blank" href="https://topenddevs.com/podcasts/elixir-mix/">Elixir Mix</a> </li>
<li><a target="_blank" href="https://abulasar.com/fetching-data-from-external-graphql-api-service-in-phoenix-liveview">Graphql Blog</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Adding Toaster message in Phoenix LiveView]]></title><description><![CDATA[This blog is about adding a Toaster message in the Phoenix LiveView application. We are going to implement this with a combination of Elixir and Javascript. Before, starting to build a toaster let's first answer the most obvious question "What is a T...]]></description><link>https://abulasar.com/adding-toaster-message-in-phoenix-liveview</link><guid isPermaLink="true">https://abulasar.com/adding-toaster-message-in-phoenix-liveview</guid><category><![CDATA[Phoenix framework]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[liveview]]></category><category><![CDATA[toast]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Tue, 21 Jun 2022 19:01:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1655837091765/XXmUR1sUa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog is about adding a Toaster message in the Phoenix LiveView application. We are going to implement this with a combination of Elixir and Javascript. Before, starting to build a toaster let's first answer the most obvious question "What is a Toast?"</p>
<h3 id="heading-what-is-a-toast">What is a Toast?</h3>
<p><em>Toasts are non-interactive, passive, and asynchronous short messages for users. Generally, they are used as an interface feedback pattern for informing the user about the results of an action.</em>
This is the most bookish answer about what is a Toast. In the easiest form, <em>it is a popup that provides simple feedback about an operation</em>. </p>
<h3 id="heading-how-our-toaster-should-work">How our toaster should work?</h3>
<p>As per definition, a toaster is a popup that provides feedback about an operation. For example, if the user tries to submit a form by clicking the <code>submit</code> button. The user should get feedback after the submission as <code>"Your account is created successfully"</code>.
In our LiveView application, we want this event(toaster display) should happen after some operation. We are going to call our toaster display logic through <code>pushEvent</code> after that operation. So, our <code>toaster</code> generation logic will be as follows</p>
<ul>
<li>After any operation in Elixir code we will trigger the <code>push_event</code></li>
<li>The <code>push_event</code> will be handled by the <code>handleEvent</code> handler in the javascript Hook</li>
<li>This <code>pushEvent</code> handler will ultimately trigger the <code>toaster</code> popup.</li>
</ul>
<p>Now, that we are ready with our roadmap let's start building 👷</p>
<h3 id="heading-finding-javascript-toaster-library">Finding javascript <code>Toaster</code> library</h3>
<p>I wanted a vanilla javascript library that should be lightweight and highly supportive in the js community. While doing some research I came across <a target="_blank" href="https://github.com/apvarun/toastify-js">toastify-js</a>. Let's install it in the application. Navigate to the <code>assets</code> directory by running <code>cd assets</code> then run </p>
<pre><code>  <span class="hljs-built_in">npm</span> install toastify-js
</code></pre><h3 id="heading-adding-the-toaster-styling">Adding the toaster styling</h3>
<ul>
<li>We need to import <code>toastify</code> styles in our <code>app.css</code> by adding the following line<pre><code><span class="hljs-keyword">@import</span> <span class="hljs-string">"../node_modules/toastify-js/src/toastify.css"</span>;
</code></pre></li>
<li>Next, we will add a javascript hook.</li>
</ul>
<h3 id="heading-adding-javascript-hook">Adding Javascript Hook</h3>
<p>After installing <code>toastify</code> we will then add the javascript logic. We will add a <code>mounted</code> callback, which is called and initialized with some initial value as soon as the element on which we are applying it is mounted in the DOM. 
As we have discussed that our toaster should be called after our elixir code will trigger <code>handle_event</code>. So, we will add our event handler in our mounted callback.</p>
<ul>
<li>Let's add a file in <code>message_toaster.js</code> in <code>assets/js</code> folder.</li>
<li>Add a mounted callback with an <code>handleEvent</code> named <code>toast</code>. </li>
</ul>
<pre><code><span class="hljs-keyword">import</span> Toastify <span class="hljs-keyword">from</span> <span class="hljs-string">'toastify-js'</span>

MessageToaster = {
  mounted() {
    <span class="hljs-built_in">this</span>.handleEvent(<span class="hljs-string">'toast'</span>, <span class="hljs-function"><span class="hljs-params">(payload)</span> =&gt;</span> {
      Toastify({
        text: payload.message,
        duration: <span class="hljs-number">3000</span>
        }).showToast();        
    })
  }
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageToaster
</code></pre><ul>
<li>We will import <code>Toastify</code> and use it in the <code>handleEvent</code>. </li>
<li>In the <code>handleEvent</code>, we have a callback function with a <code>payload</code> param. It will have a custom <code>message</code> which will be used in the <code>Toastify</code>.</li>
<li><p>At last but not least we will include our <code>MessageToaster</code> hook in our LiveSocket. For this, we will open the <code>app.js</code> file and import <code>MessageToaster</code> and include it in our Hooks object, and add the Hooks to our LiveSocket constructor as follows.</p>
<pre><code>  import MessageToaster <span class="hljs-keyword">from</span> <span class="hljs-string">"./message_toaster"</span>
  <span class="hljs-keyword">let</span> Hooks = { MessageToaster }

  <span class="hljs-keyword">let</span> liveSocket = <span class="hljs-keyword">new</span> LiveSocket(<span class="hljs-string">"/live"</span>, Socket, {<span class="hljs-keyword">params</span>: {_csrf_token: csrfToken}, hooks: Hooks }) 
  <span class="hljs-meta"># Notice `hooks: Hooks`</span>
</code></pre></li>
</ul>
<h3 id="heading-registering-the-hook">Registering the Hook</h3>
<ul>
<li>To call our Javascript logic i.e hook we’ll add the <code>phx-hook</code> attribute on the root page with value as the hook name <code>i.e MessageToaster</code>. We will add our <code>phx-hook="MessageToaster"</code> on the <code>live.html.heex</code> page.</li>
<li><p>Add the following code</p>
<pre><code># lib/project_name_web/templates/layout/live.html.heex
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">phx-hook</span>=<span class="hljs-string">"MessageToaster"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">%=</span> @<span class="hljs-attr">content</span> %&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre></li>
</ul>
<h3 id="heading-calling-the-toaster">Calling the Toaster</h3>
<ul>
<li>As discussed we are going to call our <code>toaster</code> from Elixir code after a certain event. For testing purposes, we are going to add a <code>button</code> in one of our <code>LiveView's</code> HTML code as follows<pre><code># live/user_registration_live.html.heex
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">phx-click</span>=<span class="hljs-string">"submit_form"</span>&gt;</span>Register User<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre></li>
<li>Then we will add <code>handle_event</code> for the <code>submit_form</code><pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_event</span><span class="hljs-params">(<span class="hljs-string">'submit_form'</span>, params, socket)</span></span> <span class="hljs-keyword">do</span>
  <span class="hljs-comment"># Form submission logic</span>
<span class="hljs-keyword">end</span>
</code></pre></li>
<li>In the <code>handle_event</code> after our form submission logic we will make <code>handle_event</code><pre><code>  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_event</span><span class="hljs-params">(<span class="hljs-string">"submit_form"</span>, params, socket)</span></span> <span class="hljs-keyword">do</span>
      <span class="hljs-comment"># Form submission logic</span>
      ....
      ....
      ....
     {<span class="hljs-symbol">:noreply</span>, push_event(socket, <span class="hljs-string">"toast"</span>, <span class="hljs-string">%{message: "Your record has been created successfully"}</span>)}
  <span class="hljs-keyword">end</span>
</code></pre></li>
<li>Final working DEMO is as follows</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655836413763/2RqBHagq1.gif" alt="toaster.gif" /></p>
<p>I hope you like this blog. If you have any questions then please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://i.stack.imgur.com/4bsSm.jpg">Cover Image</a></li>
<li><a target="_blank" href="https://github.com/apvarun/toastify-js">Toastify.js</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Highlighting the current page link in the Navbar menu in Phoenix LiveView]]></title><description><![CDATA[A good user experience happens when an application is easy to use and with intuitive UI feedback. Nav menu bar is one the most important element in web applications, it helps users to navigate to different pages. It's vital that users should be aware...]]></description><link>https://abulasar.com/highlighting-the-current-page-link-in-the-navbar-menu-in-phoenix-liveview</link><guid isPermaLink="true">https://abulasar.com/highlighting-the-current-page-link-in-the-navbar-menu-in-phoenix-liveview</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[navigation]]></category><category><![CDATA[menu]]></category><category><![CDATA[liveview]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sat, 11 Jun 2022 16:53:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746034630191/670144fc-f64d-4868-8945-fb9a8796bb66.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A good user experience happens when an application is easy to use and with intuitive UI feedback. <code>Nav</code> menu bar is one the most important element in web applications, it helps users to navigate to different pages. It's vital that users should be aware of which current tab on which user is. This can be achieved by highlighting the current tab user is. See the cover image of this blog to make sense.</p>
<p><strong>NOTE</strong>: Jump to <em>Highlighting the current menu (Dirty Solution)</em> section if you already have a project with Navbar.</p>
<h3 id="heading-lets-start-building">Let's start building</h3>
<ul>
<li><p>First of all, we should have a LiveView project or will create a new LiveView project by running <code>mix phx.new navigation_demo --no-ecto --live</code></p>
</li>
<li><p>We will then navigate to the project directory by running <code>cd navigation_demo</code> and run <code>mix phx.server</code>. Once, the project starts running navigate to <code>localhost:4000</code> to see the <code>Welcome</code> phoenix screen.</p>
</li>
</ul>
<h3 id="heading-adding-liveview-pages">Adding liveview pages</h3>
<ul>
<li><p>To demonstrate the navbar highlighting logic in Elixir, we will first add three items to the navbar menu with minimal functionality.</p>
</li>
<li><p>For this open <code>router.ex</code> and following routes</p>
</li>
</ul>
<pre><code class="lang-elixir">live <span class="hljs-string">"/"</span>, HomePageLive, <span class="hljs-symbol">:index</span>
live <span class="hljs-string">"/page1"</span>, Page1Live, <span class="hljs-symbol">:index</span>
live <span class="hljs-string">"/page2"</span>, Page2Live, <span class="hljs-symbol">:index</span>
</code></pre>
<ul>
<li><p>Now go to live folder and add three files <code>home_page_live.ex</code>, <code>page1_live.ex</code>, and <code>page2_live.ex</code>.</p>
</li>
<li><p>Add <code>mount</code> and <code>render</code> callbacks in each of the following with minimal text. Refer following code for reference</p>
</li>
</ul>
<pre><code class="lang-sql">   // home_page_live.ex
   defmodule NavigationDemoWeb.HomePageLive <span class="hljs-keyword">do</span>
      <span class="hljs-keyword">use</span> NavigationDemoWeb, :live_view

      <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(_params, _session, socket) <span class="hljs-keyword">do</span>
        {:ok, socket}
      <span class="hljs-keyword">end</span>

      <span class="hljs-keyword">def</span> render(assigns) <span class="hljs-keyword">do</span>
         ~H<span class="hljs-string">"""
            Home page
         """</span>
     <span class="hljs-keyword">end</span>
   <span class="hljs-keyword">end</span>

   // page1_live.ex
   defmodule NavigationDemoWeb.Page1Live <span class="hljs-keyword">do</span>
      <span class="hljs-keyword">use</span> NavigationDemoWeb, :live_view

      <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(_params, _session, socket) <span class="hljs-keyword">do</span>
        {:ok, socket}
      <span class="hljs-keyword">end</span>

      <span class="hljs-keyword">def</span> render(assigns) <span class="hljs-keyword">do</span>
       ~H<span class="hljs-string">"""
          Page 1
        """</span>
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>

    // page2_live.ex
    defmodule NavigationDemoWeb.Page2Live <span class="hljs-keyword">do</span>
        <span class="hljs-keyword">use</span> NavigationDemoWeb, :live_view

        <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(_params, _session, socket) <span class="hljs-keyword">do</span>
          {:ok, socket}
        <span class="hljs-keyword">end</span>

        <span class="hljs-keyword">def</span> render(assigns) <span class="hljs-keyword">do</span>
         ~H<span class="hljs-string">"""
            Page 2
         """</span>
        <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li>Adding of LiveView pages is done, now we will add a navigation menu with minimal styling.</li>
</ul>
<h3 id="heading-add-navbar-menu">Add Navbar menu</h3>
<ul>
<li><p>Before adding the Navbar menu we will do some cleanup in our existing code.</p>
</li>
<li><p>Open <code>templates/layout/root.html.heex</code> and remove all code in the body tag and just add the following code</p>
</li>
</ul>
<pre><code class="lang-sql">  &lt;body&gt;
      &lt;%= @inner_content %&gt;        
  &lt;/body&gt;
</code></pre>
<ul>
<li>Open the <code>live.html.heex</code> file and add the following code</li>
</ul>
<pre><code class="lang-elixir">  &lt;nav&gt;
     &lt;h3&gt;NavBar Test&lt;<span class="hljs-regexp">/h3&gt;
     &lt;ul&gt;
        &lt;li&gt;&lt;%= live_patch "Home", to: Routes.home_page_path(socket, :index)%&gt;&lt;/li</span>&gt;
        &lt;li&gt;&lt;%= live_patch <span class="hljs-string">"Page 1"</span>, <span class="hljs-symbol">to:</span> Routes.page1_path(socket, <span class="hljs-symbol">:index</span>)%&gt;&lt;<span class="hljs-regexp">/li&gt;
        &lt;li&gt;&lt;%= live_patch "Page 2", to: Routes.page2_page_path(socket, :index)%&gt;&lt;/li</span>&gt;
      &lt;<span class="hljs-regexp">/ul&gt;
  &lt;/nav</span>&gt;
</code></pre>
<pre><code>
* Open <span class="hljs-string">`app.css`</span> and add the following styling

    <span class="hljs-string">``</span><span class="hljs-string">`css
    nav { display: flex; justify-content:space-between; align-items: center; padding: 1rem 2rem; background: #cfd8dc; }

    nav ul { display: flex; list-style: none; }

    nav li { padding-left: 1rem; }

    nav a { text-decoration: none; color: lightblue }

    .active { background: red; }</span>
</code></pre><ul>
<li><p>The resulting home page will be as follows</p>
<p>  ![Screenshot 2022-06-11 at 12.34.14 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1654931128407/bdL8-r1Wh.png align="left")</p>
</li>
</ul>
<p>But, now here the problem starts if you will notice the `home` page in the navbar does not have any kind of indication that it is the currently selected menu.</p>
<h3 id="heading-highlighting-the-current-menu-dirty-solution">Highlighting the current menu (Dirty Solution)</h3>
<ul>
<li>The easiest solution can be using the `handle_params` callback in each LiveView file and assigning some variable say `current_path` to the socket struct which will be accessible in the `live.html.heex`</li>
</ul>
<pre><code class="lang-elixir"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_params</span></span>(_params, url, socket) 
   {<span class="hljs-symbol">:noreply</span>, socket |&gt; assign(current_path, URI.parse(url).path)} 
<span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p>In the <code>live.html.heex</code>, add the following logic</p>
<p>  ```</p>
<pre><code class="lang-elixir">  &lt;%= live_patch <span class="hljs-string">"Home"</span>, <span class="hljs-symbol">to:</span> Routes.home_page_path(socket, <span class="hljs-symbol">:index</span>)%&gt;

  &lt;%= live_patch <span class="hljs-string">"Page 1"</span>, <span class="hljs-symbol">to:</span> Routes.page1_path(socket, <span class="hljs-symbol">:index</span>)%&gt;

  &lt;%= live_patch <span class="hljs-string">"Page 2"</span>, <span class="hljs-symbol">to:</span> Routes.page2_page_path(socket, <span class="hljs-symbol">:index</span>)%&gt;
</code></pre>
</li>
<li><p>This will work, but it will become very cumbersome when there are more tabs. Also, this approach is not DRY.</p>
</li>
</ul>
<h3 id="heading-highlighting-the-current-menu-improved-solution">Highlighting the current menu (Improved Solution)</h3>
<ul>
<li><p>The above solution is not very DRY. We can improve this code using the concept of <code>on_mount</code> and <code>attach_hook</code>.</p>
</li>
<li><p>We want that every time when the user changes the menu in our app it should trigger <code>handle_params</code>. Also, we don't want <code>handle_params</code> to be placed in every <code>LiveView</code>.</p>
</li>
<li><p>We can add custom behavior to other callbacks using <code>attach_hook</code>.</p>
</li>
<li><p>So, we'll register this callback i.e <code>handle_params</code> to the socket struct, something like this.</p>
</li>
</ul>
<pre><code class="lang-elixir">socket |&gt; attach_hook(<span class="hljs-symbol">:set_menu_path</span>, <span class="hljs-symbol">:handle_params</span>, &amp;manage_active_tabs/<span class="hljs-number">3</span>)
</code></pre>
<ul>
<li><p>In the above code,</p>
<ul>
<li><p><code>:set_menu_path</code> is the hook name</p>
</li>
<li><p><code>:handle_params</code> is the callback</p>
</li>
<li><p><code>manage_active_tabs</code> is the function body of <code>handle_params</code></p>
</li>
</ul>
</li>
<li><p>This hook will be assigned to <code>socket</code> struct and this will be done in the <code>on_hook</code> function.</p>
</li>
<li><p>We'll attach these hooks via Phoenix.LiveView.Router.live_session.</p>
</li>
<li><p>We'll add a file <code>live/route_assigns.ex</code>, and we'll mount the <code>RouteAssigns</code> module using <code>on_mount</code> in the <code>router.ex</code> as follows</p>
</li>
</ul>
<pre><code class="lang-sql">live_session :default, `on_mount: NavigationDemoWeb.RouteAssigns` <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">scope</span> <span class="hljs-string">"/"</span>, NavigationDemoWeb <span class="hljs-keyword">do</span>
        pipe_through :browser
        live <span class="hljs-string">"/"</span>, HomePageLive, :<span class="hljs-keyword">index</span>
        live <span class="hljs-string">"/page1"</span>, Page1Live, :<span class="hljs-keyword">index</span>
        live <span class="hljs-string">"/page2"</span>, Page2Live, :<span class="hljs-keyword">index</span>
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p>In <code>on_mount</code> you have access to socket struct. We will assign a <code>menus</code> key with value as a list of tuples of <code>menu name</code> and the <code>route</code> of the menu.</p>
</li>
<li><p>Refer to the following code</p>
</li>
</ul>
<pre><code class="lang-sql">defmodule NavigationDemoWeb.RouteAssigns <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">import</span> Phoenix.LiveView
    <span class="hljs-keyword">alias</span> NavigationDemoWeb.Router.Helpers, <span class="hljs-keyword">as</span>: Routes

    <span class="hljs-keyword">def</span> on_mount(:<span class="hljs-keyword">default</span>, _params, _session, socket) <span class="hljs-keyword">do</span>
      socket =
        assign(socket,
          menus: [
            {<span class="hljs-string">"Home"</span>, Routes.home_page_path(socket, :<span class="hljs-keyword">index</span>)},
            {<span class="hljs-string">"Page 1"</span>, Routes.page1_path(socket, :<span class="hljs-keyword">index</span>)},
            {<span class="hljs-string">"Page 2"</span>, Routes.page2_path(socket, :<span class="hljs-keyword">index</span>)}
          ]
        )

      {:cont,
        socket
        |&gt; attach_hook(:set_menu_path, :handle_params, &amp;manage_active_tabs/<span class="hljs-number">3</span>)
        }
    <span class="hljs-keyword">end</span>

    defp manage_active_tabs(_params, <span class="hljs-keyword">url</span>, socket) <span class="hljs-keyword">do</span>
      {:cont, assign(socket, current_path: URI.parse(<span class="hljs-keyword">url</span>).path)}
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p>Notice <code>cont</code> in the return tuple of <code>on_mount</code> and <code>manage_active_tabs</code> functions. The hook has the option to either halt or continue the process.</p>
</li>
<li><p>In our <code>handle_params</code> callback function i.e<code>manage_active_tabs</code> we will assign <code>current_path</code> field to <code>socket</code> struct. This field will help us to select the current menu.</p>
</li>
<li><p>Because of assigning <code>menus</code> to the socket variable it will be accessible in the <code>live.html.heex</code> file.</p>
</li>
<li><p>We can iterate over it i.e <code>menus</code>, with the first element of the tuple as the <code>link name</code> and the second element as the <code>path</code>. Refer to the code below</p>
</li>
</ul>
<pre><code class="lang-sql">// live.html.heex
&lt;ul&gt;
    &lt;%= for {menu_name, path} &lt;- @menus <span class="hljs-keyword">do</span> %&gt;
      &lt;li <span class="hljs-keyword">class</span>={<span class="hljs-string">"#{if path == @current_path, do: "</span>active<span class="hljs-string">"}"</span>}&gt;
        &lt;%= live_patch menu_name, <span class="hljs-keyword">to</span>: <span class="hljs-keyword">path</span> %&gt;
      &lt;/li&gt;
    &lt;% <span class="hljs-keyword">end</span> %&gt;
&lt;/ul&gt;
</code></pre>
<ul>
<li><p>In the above code, you can notice the <code>if</code> condition which applies <code>active</code> if <code>@current_path</code> is equal to the path of the menu.</p>
</li>
<li><p>The <code>active</code> class applied a background of <code>red</code> color to highlight the selected menu.</p>
</li>
</ul>
<p>I know the blog was very conceptual with lots of information. I hope I did justice with the explanation and you like this blog. If you have any questions then please comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Adding Infinite scroll in Phoenix LiveView App]]></title><description><![CDATA[In my previous blog, I explained how to query Graphql API using Neuron. Throughout the blog, I explained the working of Neuron on page 0. Whereas, there will be scenarios when users can have multiple blogs paginated on multiple pages. So, we need to ...]]></description><link>https://abulasar.com/adding-infinite-scroll-in-phoenix-liveview-app</link><guid isPermaLink="true">https://abulasar.com/adding-infinite-scroll-in-phoenix-liveview-app</guid><category><![CDATA[infinite scrolling]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Tue, 07 Jun 2022 08:45:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1654535103162/Nv2l_aJgu.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my <a target="_blank" href="https://abulasar.com/fetching-data-from-graphql-api-in-phoenix-liveview-using-neuron">previous</a> blog, I explained how to query Graphql API using Neuron. Throughout the blog, I explained the working of Neuron on page <code>0</code>. Whereas, there will be scenarios when users can have multiple blogs paginated on multiple pages. So, we need to add functionality so that users can load multiple pages. This can be achieved easily by adding a button with some text <code>Load More</code> and adding an event to <code>load</code> the data on the click. But we will be using a little different approach of using paginating using <code>Infinite Scroll</code>. This will be a continuation of my previous blog, so let's add this feature.</p>
<h3 id="heading-what-is-infinite-scroll">What is Infinite Scroll?</h3>
<p>Before we start, we first need to understand how Infinite scroll works. To get a fair idea about Infinite Scroll, refer to the gif image below.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1654528974404/P9sc1476O.gif" alt="infinite-scroll.gif" /></p>
<p>This is similar to what we see on Youtube, Facebook, Instagram, etc. It keeps user engage in the content and also keep away user from the hassle of clicking the <code>Next</code> button to load content.</p>
<h3 id="heading-how-infinite-scroll-works">How Infinite Scroll works?</h3>
<p>Infinite Scroll logic is very simple, we just have to add a javascript event listener which will listen to the <code>scroll</code> event. It will get triggered as soon as we reach the bottom (near the bottom) of the page. Below is the logic to detect the bottom of the page.</p>
<pre><code>let scrollAt <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span>
  let scrollTop <span class="hljs-operator">=</span> document.documentElement.scrollTop <span class="hljs-operator">|</span><span class="hljs-operator">|</span> document.body.scrollTop
  let scrollHeight <span class="hljs-operator">=</span> document.documentElement.scrollHeight <span class="hljs-operator">|</span><span class="hljs-operator">|</span> document.body.scrollHeight
  let clientHeight <span class="hljs-operator">=</span> document.documentElement.clientHeight
  <span class="hljs-keyword">return</span> scrollTop <span class="hljs-operator">/</span> (scrollHeight <span class="hljs-operator">-</span> clientHeight) <span class="hljs-operator">*</span> <span class="hljs-number">100</span>
}
</code></pre><p>We just need to call this logic in the proper LiveView lifecycle and this can be achieved with LiveView JS Hooks.</p>
<h3 id="heading-what-is-js-hook">What is JS hook?</h3>
<p>Phoenix LiveView often markets itself as that we can create reactive apps without writing JavaScript. But still, there comes a time when you have to write javascript. To integrate Javascript in our LiveView app, LiveView provides us with a concept of LiveView JS Hooks. JS Hooks allow us to register JavaScript lifecycle callbacks on DOM nodes.</p>
<h3 id="heading-how-does-js-hook-work">How does JS Hook work?</h3>
<p>There are different types of lifecycle callbacks like <code>mounted</code>, <code>reconnected</code>, and <code>updated</code>. The <code>mounted</code> is the first callback that is called and initialized with some initial value as soon as the element on which we are applying it is mounted in the DOM. So, here we will add our <code>addEventListener</code> and initialize some value. We will create a new file for our hook file in <code>assets/js/infinite_list.js</code> and add the following code.</p>
<pre><code>let scrollAt <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  let scrollTop <span class="hljs-operator">=</span> document.documentElement.scrollTop <span class="hljs-operator">|</span><span class="hljs-operator">|</span> document.body.scrollTop
  let scrollHeight <span class="hljs-operator">=</span> document.documentElement.scrollHeight <span class="hljs-operator">|</span><span class="hljs-operator">|</span> document.body.scrollHeight
  let clientHeight <span class="hljs-operator">=</span> document.documentElement.clientHeight

  <span class="hljs-keyword">return</span> scrollTop <span class="hljs-operator">/</span> (scrollHeight <span class="hljs-operator">-</span> clientHeight) <span class="hljs-operator">*</span> <span class="hljs-number">100</span>
}

InfiniteList <span class="hljs-operator">=</span> {
  page() { <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.el.dataset.page },
  mounted(){
    <span class="hljs-built_in">this</span>.pending <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.page()
    window.addEventListener(<span class="hljs-string">"scroll"</span>, e <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
      <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.pending <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.page() <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> scrollAt() <span class="hljs-operator">&gt;</span> <span class="hljs-number">90</span>){
        <span class="hljs-built_in">this</span>.pending <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.page() <span class="hljs-operator">+</span> <span class="hljs-number">1</span>
        <span class="hljs-built_in">this</span>.pushEvent(<span class="hljs-string">"load-blogs"</span>, {})
      }
    })
  },
  reconnected(){ <span class="hljs-built_in">this</span>.pending <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.page() },
  updated(){ <span class="hljs-built_in">this</span>.pending <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.page() }
}

export default InfiniteList
</code></pre><p>As discussed <code>mounted</code> callback is a place where we will add <code>eventListener</code> logic. It detects page bottom and triggers <code>pushEvent</code> of <code>load-blogs</code>. This will call <code>handle_event</code> callback in our <code>lib/fetch_hashnode_web/live/blogs_live.ex</code> which will have the following code logic.</p>
<pre><code>def handle_event(<span class="hljs-string">"load-blogs"</span>, _params, socket) do
    page_no <span class="hljs-operator">=</span> socket.assigns.page_no <span class="hljs-operator">+</span> <span class="hljs-number">1</span>
    new_blogs <span class="hljs-operator">=</span> page_no <span class="hljs-operator">|</span><span class="hljs-operator">&gt;</span> Blog.get_blogs
    blogs <span class="hljs-operator">=</span> socket.assigns.blogs <span class="hljs-operator">+</span><span class="hljs-operator">+</span> new_blogs
    socket <span class="hljs-operator">=</span> assign(socket, blogs: blogs, page_no: page_no)
    {:noreply, socket}
end
</code></pre><p>The logic here is self-explanatory we are fetching blogs using <code>Blog.get_blogs</code> by passing incremented <code>page_no</code> to it and assigning the <code>blogs</code> to the <code>socket</code> struct. </p>
<h3 id="heading-adding-temporaryassigns">Adding <code>temporary_assigns</code></h3>
<p>One last change we have to make in the <code>blogs_live.ex</code> code. We have to use <code>temporary_assigns</code> for our <code>blogs</code> field in the <code>mount</code> callback. When we assign new blogs to <code>socket.assigns.blogs</code> there are chances that our data can grow, which will increase data in the socket, and ultimately our application performance will be affected. Refer to the following change</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mount</span><span class="hljs-params">(_params, _session, socket)</span></span> <span class="hljs-keyword">do</span>
    blogs = Blog.get_blogs()
    socket = assign(socket, <span class="hljs-symbol">blogs:</span> blogs, <span class="hljs-symbol">page_no:</span> <span class="hljs-number">0</span>)
    {<span class="hljs-symbol">:ok</span>, socket, <span class="hljs-symbol">temporary_assigns:</span> [<span class="hljs-symbol">blogs:</span> []]}
<span class="hljs-keyword">end</span>
</code></pre><h3 id="heading-adding-the-hook-in-livesocket">Adding the hook in LiveSocket</h3>
<p>Now we will include our <code>InfiniteList</code> hook in our LiveSocket. For this, we will open the <code>app.js</code> file and import <code>InfiniteList</code> and include it in our Hooks object, and add the Hooks to our LiveSocket constructor.</p>
<pre><code>import InfiniteList <span class="hljs-keyword">from</span> <span class="hljs-string">"./infinite_list"</span>

<span class="hljs-keyword">let</span> Hooks = { InfiniteList }
....
<span class="hljs-keyword">let</span> liveSocket = <span class="hljs-keyword">new</span> LiveSocket(<span class="hljs-string">"/live"</span>, Socket, {<span class="hljs-keyword">params</span>: {_csrf_token: csrfToken}, hooks: Hooks }) 
<span class="hljs-comment">// Notice `hooks: Hooks`</span>
</code></pre><p>Now that we have added our <code>InfiniteList</code> hook to our hooks list. We will call this hook in our final arrangement.</p>
<h3 id="heading-calling-the-hook">Calling the Hook</h3>
<p>To call our Javascript logic i.e hook we’ll add the <code>phx-hook</code> attribute on the <code>div</code> with value as the hook name i.e <code>InfiniteList</code> where we want to perform scrolling.</p>
<pre><code><span class="hljs-operator">&lt;</span>div class<span class="hljs-operator">=</span><span class="hljs-string">"blogs"</span> id<span class="hljs-operator">=</span><span class="hljs-string">"infinite-list"</span> phx<span class="hljs-operator">-</span>hook<span class="hljs-operator">=</span><span class="hljs-string">"InfiniteList"</span> phx<span class="hljs-operator">-</span>update<span class="hljs-operator">=</span><span class="hljs-string">"append"</span> data<span class="hljs-operator">-</span>page<span class="hljs-operator">=</span>{@page_no}<span class="hljs-operator">&gt;</span>
   <span class="hljs-operator">&lt;</span><span class="hljs-operator">%</span><span class="hljs-operator">=</span> <span class="hljs-keyword">for</span> blog <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> @blogs do <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-comment">// Display logic</span>
   <span class="hljs-operator">&lt;</span><span class="hljs-operator">%</span> end <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
</code></pre><p>You will also notice the <code>data-page</code> attribute which has the value of <code>@page_no</code>. This attribute is the reason, that we were able to access the <code>page</code> number in the <code>page</code> method of the <code>InfiniteScroll</code> hook.</p>
<pre><code>InfiniteList <span class="hljs-operator">=</span> {
  page() { <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.el.dataset.page },
  ...
</code></pre><p>The <code>phx-update=append</code> helps us to append or prepend the updates rather than replacing the existing contents. And don't miss to add the <code>id</code> attribute as well on the div. So, the following is the list of attributes that we need to apply on the div to enable javascript interactions.</p>
<ul>
<li>Hooks (<code>phx-hook="InfiniteList"</code>)</li>
<li>phx-update (<code>phx-update=append</code>)</li>
<li>data attribute (<code>data-page={@page_no}</code>)</li>
<li>id (<code>id='infinite-list'</code>)</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>For performing Infinite scroll we have to </p>
<ul>
<li>Create hooks</li>
<li>Add it to the hooks list in <code>LiveSocket</code> object</li>
<li>Add <code>hook</code> on the div where you want to perform scrolling so that it can communicate with the javascript.</li>
</ul>
<p>I hope you like this blog. If you have any questions then please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://infinite-scroll.com/img/infinite-scroll-illo.png">Cover Image</a></li>
<li><a target="_blank" href="https://dribbble.com/shots/1876839-Infinite-Scroll">Infinite Scroll GIF</a></li>
<li><a target="_blank" href="https://elixirforum.com/t/how-can-i-implement-an-infinite-scroll-in-liveview/30457/2">JS Logic for detecting bottom screen</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Fetching data from external Graphql API service in Phoenix LiveView]]></title><description><![CDATA[Recently while creating my personal portfolio website abulasar.dev, I added a blog page. For that, I had fetched hashnode blogs on the page. Hashnode has exposed its blog through API. You can visit api.hashnode.com and this API is in Graphql format. ...]]></description><link>https://abulasar.com/fetching-data-from-external-graphql-api-service-in-phoenix-liveview</link><guid isPermaLink="true">https://abulasar.com/fetching-data-from-external-graphql-api-service-in-phoenix-liveview</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sun, 29 May 2022 19:34:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1653852633621/brhQ0WWo8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently while creating my personal portfolio website <a target="_blank" href="http://abulasar.dev/">abulasar.dev</a>, I added a <code>blog</code> page. For that, I had fetched <a target="_blank" href="https://hashnode.com/">hashnode</a> blogs on the page. <a target="_blank" href="https://hashnode.com/">Hashnode</a> has exposed its blog through <a target="_blank" href="https://api.hashnode.com/">API</a>. You can visit <code>api.hashnode.com</code> and this API is in Graphql format. Previously, I had fetched these blogs in an Ember.js app and wrote a <a target="_blank" href="https://abulasar.com/fetching-hashnode-blogs-in-ember-app">blog</a> on this topic. I had no previous experience using graphql client in the elixir projects. I implemented it in <a target="_blank" href="http://abulasar.dev/">this</a> project and this blog will be about using graphql query in the Phoenix LiveView project. Let's build one!!</p>
<h3 id="heading-action-plan">Action Plan</h3>
<ul>
<li>Creating LiveView project</li>
<li>Installing and configuring graphql client.</li>
<li>Adding API layer to fetch data.</li>
<li>Integrating API layer with UI</li>
</ul>
<h3 id="heading-lets-start-building">Let's start building 👷</h3>
<ul>
<li>First of all, as discussed will create a LiveView project. </li>
<li>We will confirm if Elixir is installed by running <code>elixir -v</code>.</li>
<li>Once confirmed we need to create/generate the Phoenix LiveView application.</li>
<li>This can be done by running <code>mix phx.new fetch_hashnode --no-ecto --live</code> and this will generate one.</li>
<li>We will then navigate to the project directory by running <code>cd fetch_hashnode</code> and run <code>mix phx.server</code>. Once, the project starts running navigate to <code>localhost:4000</code> to see the <code>Welcome</code> phoenix screen.</li>
</ul>
<h3 id="heading-installing-graphql-client">Installing Graphql client</h3>
<ul>
<li>I searched for Graphql client in Elixir and came across a few but the one that caught my eye is <a target="_blank" href="https://github.com/uesteibar/neuron">Neuron</a>. It is very easy to configure and use so I decided to go with <code>Neuron</code>.</li>
<li>We will configure <code>Neuron</code> by first adding it to the list of dependencies.</li>
<li>Open the <code>mix.exs</code> file and add Neuron to the list of dependencies. <pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deps</span> <span class="hljs-title">do</span></span>
[
 ........,
  {<span class="hljs-symbol">:neuron</span>, <span class="hljs-string">"~&gt; 5.0.0"</span>}
]
<span class="hljs-keyword">end</span>
</code></pre></li>
<li>Now run <code>mix deps.get</code> to install the dependency.</li>
<li>Next, we are going to configure <code>Neuron</code>.</li>
</ul>
<h3 id="heading-configure-neuron">Configure Neuron</h3>
<ul>
<li>To query Hashnode API, we have to add a <code>url</code> in the Neuron configuration.</li>
<li>As per documentation we can test Neuron in <code>iex shell</code> by running the following query.</li>
</ul>
<pre><code>iex<span class="hljs-operator">&gt;</span> Neuron.Config.set(url: <span class="hljs-string">"https://api.hashnode.com/"</span>)
</code></pre><ul>
<li>After adding this you can query Hasnode API as follows</li>
</ul>
<pre><code>Neuron.query(<span class="hljs-string">"""
{
  user(username: "your_username") {
      publication {
          posts(page: 0) {
              title
              brief
              slug
              cuid
              coverImage
          }
      }
  }
}
"""</span>)
</code></pre><ul>
<li>This query will return a <code>Neuron</code> response with a list of blogs on page number 0, along with the nitty-gritty details of <code>header</code> responses, something like below.</li>
</ul>
<pre><code>{:ok,
 <span class="hljs-operator">%</span>Neuron.Response{
   body: <span class="hljs-operator">%</span>{
     <span class="hljs-string">"data"</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">%</span>{
       <span class="hljs-string">"user"</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">%</span>{
         <span class="hljs-string">"publication"</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-operator">%</span>{
           <span class="hljs-string">"posts"</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> [
             <span class="hljs-operator">%</span>{ } <span class="hljs-comment">// blog 1,</span>
             <span class="hljs-operator">%</span>{ } <span class="hljs-comment">// blog 2</span>
           ]
    }
           ]
         }
       }
     }
   },
   headers: [
     {<span class="hljs-string">"Connection"</span>, <span class="hljs-string">"keep-alive"</span>},
     {<span class="hljs-string">"Content-Length"</span>, <span class="hljs-string">"3186"</span>},
     ......
   ],
   status_code: <span class="hljs-number">200</span>
 }
}
</code></pre><ul>
<li>So far so good, everything is looking fine. In our next step, we'll add an API layer where we'll place the above query to fetch the data.</li>
</ul>
<h3 id="heading-adding-api-layer-to-fetch-data">Adding API layer to fetch data</h3>
<ul>
<li>We will add a module that will have all two functions one to fetch all blogs and one to fetch specific blogs.</li>
<li>Create a file a create a folder <code>blog_posts</code> under <code>lib/fetch_hashnode</code>. In that, we'll add the <code>blog.ex</code> file.</li>
<li><p>Add the following code to it.</p>
<pre><code>defmodule  FetchHashnode.Blog <span class="hljs-keyword">do</span>
  @username <span class="hljs-string">"your_hashnode_username"</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_blogs</span><span class="hljs-params">(page \\<span class="hljs-number">0</span>)</span></span> <span class="hljs-keyword">do</span>
    Neuron.query(<span class="hljs-string">""</span><span class="hljs-string">"
      {
        user(username: "</span><span class="hljs-comment">#{<span class="hljs-doctag">@username</span>}") {</span>
          publication {
              posts(<span class="hljs-symbol">page:</span> <span class="hljs-comment">#{page}) {</span>
                  title
                  brief
                  slug
                  cuid
                  coverImage
              }
          }
        }
      }
    <span class="hljs-string">""</span><span class="hljs-string">")
  end

  def get_detail_blog(slug) do
    Neuron.query("</span><span class="hljs-string">""</span>
      {
          post(<span class="hljs-symbol">slug:</span> <span class="hljs-string">"<span class="hljs-subst">#{slug}</span>"</span>, <span class="hljs-symbol">hostname:</span> <span class="hljs-string">"<span class="hljs-subst">#{@username}</span>"</span>) {
          title,
          slug,
          cuid,
          coverImage,
          content,
          contentMarkdown,
          tags {
          name
          }
       }
     }
    <span class="hljs-string">""</span><span class="hljs-string">")
 end
end</span>
</code></pre></li>
<li>The above two functions are self-explanatory. It is simple and very similar to what we have tried earlier in the console.</li>
<li>Let's fire up the <code>iex</code> shell by running <code>iex -S mix phx.server</code> and do some testing of these functions.</li>
<li>We'll first test the <code>get_blogs</code> function which expects to return a list of all blogs on page 0 (because the default is 0).</li>
<li>Run the following query<pre><code><span class="hljs-selector-tag">iex</span>&gt;  <span class="hljs-selector-tag">FetchHashnode</span><span class="hljs-selector-class">.Blog</span><span class="hljs-selector-class">.get_blogs</span>
</code></pre></li>
<li>We will get the error <code>" you need to supply an url"</code>. So, what went wrong? 🤔</li>
<li>If you'll remember we registered the <code>url</code> with the <code>Neuron</code> in the shell earlier by doing something like this.<pre><code><span class="hljs-selector-tag">Neuron</span><span class="hljs-selector-class">.Config</span><span class="hljs-selector-class">.set</span>(<span class="hljs-attribute">url</span>: <span class="hljs-string">"https://api.hashnode.com/"</span>)
</code></pre></li>
<li>We want <code>Neuron</code> should be aware of this <code>url</code> as soon as the server starts. So, we will add the above snippet in <code>application.ex</code> in the <code>start</code> function.</li>
</ul>
<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span><span class="hljs-params">(_type, _args)</span></span> <span class="hljs-keyword">do</span>
    children = [
      FetchHashnode.Repo,
      ....
   ]
   opts = [<span class="hljs-symbol">strategy:</span> <span class="hljs-symbol">:one_for_one</span>, <span class="hljs-symbol">name:</span> FetchHashnode.Supervisor]

   Neuron.Config.set(<span class="hljs-symbol">url:</span> <span class="hljs-string">"https://api.hashnode.com/"</span>)  <span class="hljs-comment"># This is the line</span>
   ...
<span class="hljs-keyword">end</span>
</code></pre><ul>
<li>Now, again restart the server <code>iex -S mix phx.server</code> and again query the function <code>FetchHashnode.Blog.get_blogs</code> and voila!! it's working now.</li>
<li>Similarly for the second function i.e <code>get_detail_blog</code> we need to pass the <code>slug</code> of the blog. So, we will test the function with some <code>slug</code> like this </li>
</ul>
<pre><code>FetchHashnode.Blog.get_detail_blog(<span class="hljs-string">'writing-mix-task-in-elixir-phoenix'</span>)
</code></pre><ul>
<li>This will return a detailed version of the blog.</li>
</ul>
<h3 id="heading-integrating-api-layer-with-ui">Integrating API layer with UI</h3>
<ul>
<li>Now, that we have a working API layer. We will add a <code>LiveView</code> page to display these fetched blogs.</li>
<li>Add <code>blogs</code> endpoint in <code>router.ex</code> by adding the following code </li>
</ul>
<pre><code>scope <span class="hljs-string">"/"</span>, FetchHashnodeWeb <span class="hljs-keyword">do</span>
    pipe_through <span class="hljs-symbol">:browser</span>

    live <span class="hljs-string">"/blogs"</span>, BlogsLive
<span class="hljs-keyword">end</span>
</code></pre><ul>
<li><p>Now add a file <code>lib/fetch_hashnode_web/live/blogs_live.ex</code> and add the following <code>mount</code> callback in the code.</p>
<pre><code>defmodule FetchHashnodeWeb.BlogsLive <span class="hljs-keyword">do</span>
 use FetchHashnodeWeb, <span class="hljs-symbol">:live_view</span>
 <span class="hljs-keyword">alias</span> FetchHashnode.Blog

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mount</span><span class="hljs-params">(_params, _session, socket)</span></span> <span class="hljs-keyword">do</span>
   {<span class="hljs-symbol">:ok</span>, blogs} = Blog.get_blogs()
   blogs = get_in(blogs.body, [<span class="hljs-string">"data"</span>, <span class="hljs-string">"user"</span>, <span class="hljs-string">"publication"</span>, <span class="hljs-string">"posts"</span>])
   socket = assign(socket, <span class="hljs-symbol">blogs:</span> blogs )
   {<span class="hljs-symbol">:ok</span>, socket}
 <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre></li>
<li>We have aliased the <code>Blog</code> module and queried the <code>get_blogs</code> function in the mount function. </li>
<li>We have assigned the fetched blogs to the <code>blogs</code> variable using pattern matching.</li>
<li>Later we have extracted all the blogs and assigned them to <code>socket</code> this will make it available in the view in the <code>render</code> callback.</li>
<li>We will loop over the <code>blogs</code> variable in the view to display something like this.<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">render</span><span class="hljs-params">(assigns)</span></span> <span class="hljs-keyword">do</span>
   ~L<span class="hljs-string">""</span><span class="hljs-string">"
      &lt;%= for blog &lt;- @blogs do %&gt;
         Title: &lt;%= blog["</span>title<span class="hljs-string">"] %&gt; 
         &lt;hr&gt;
      &lt;% end %&gt;
   "</span><span class="hljs-string">""</span>
<span class="hljs-keyword">end</span>
</code></pre></li>
<li>It will look something like below
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653852063407/DDw-OEZVN.png" alt="Screenshot 2022-05-30 at 12.50.58 AM.png" /></li>
<li>Similarly, we can fetch a particular blog and display it on a separate <code>LiveView</code> page. I'll leave this logic up to you 😊.</li>
</ul>
<p>I hope you like this blog. Of course, there is some scope of refactoring that I intentionally didn't touch. If you have any questions then please comment below. Thanks for reading 😊.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><a target="_blank" href="https://github.com/uesteibar/neuron">Neuron</a></li>
<li><a target="_blank" href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html">LiveView</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Building Realtime LiveView App using Phoenix PubSub]]></title><description><![CDATA[In my previous blog on the Note-taking app, we missed harnessing the most important feature of Phoenix LiveView i.e real-time update. What does that mean? It means to do an update in your application screen without explicitly refreshing your applicat...]]></description><link>https://abulasar.com/building-realtime-liveview-app-using-phoenix-pubsub</link><guid isPermaLink="true">https://abulasar.com/building-realtime-liveview-app-using-phoenix-pubsub</guid><category><![CDATA[Phoenix framework]]></category><category><![CDATA[Elixir]]></category><category><![CDATA[socket]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sat, 06 Nov 2021 07:13:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636182708121/JC8KpAKao.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my previous blog on the <a target="_blank" href="https://abulasar.com/creating-note-taking-app-using-liveview-and-genserver-part-1">Note-taking</a> app, we missed harnessing the most important feature of Phoenix LiveView i.e real-time update. What does that mean? It means to do an update in your application screen without explicitly refreshing your application. For example, in a chat app, you don't have to explicitly refresh your screen to see the change in the screen. In our previous version of the Notetaking app when we add the note in the screen and if there is a browser screen opened parallel with the existing one then that new note won't be reflected, you have to refresh the browser to see the result. In this blog, we are going to add this new feature to our previous blog code.
Refer to the <code>gif</code> below for the complete real-time update version of the app that we are going to make.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636170477936/CsERKZf9o.gif" alt="realtime_note_app.gif" /></p>
<h3 id="how-pubsub-works">How PubSub works?  🤔</h3>
<p>Phoenix PubSub is an API that is responsible for doing live updates in the app. The documentation itself says it's a <code>Realtime Publisher/Subscriber service</code>. In application, our LiveView(module) process subscribes to a particular channel(s). When a message is published on a topic, the message is broadcast. Similarly, when a message is published from a topic, the message is broadcast to all the subscribers of that topic. When the broadcast message is received to the subscribed Process their state is updated which ultimately re-renders the view. Below diagram will give you an idea about subscribers and </p>
<h5 id="subscriber">Subscriber</h5>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636176261086/BkC0MOsgr.png" alt="subscriber.png" /></p>
<h5 id="publisher">Publisher</h5>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636177005253/KLvf-xrHc.png" alt="publisher.png" /></p>
<h3 id="adding-the-subscriber">Adding the Subscriber</h3>
<ul>
<li>We've understood the concept of the subscriber. It will be a function that will be called by the LiveView module.</li>
<li>The <code>Phoenix.PubSub</code> module has a <code>subscribe</code> function that takes 2 parameters. <ul>
<li>The PubSub instance of our app, in our case it is <code>NoteApp.PubSub</code></li>
<li>The name of the channel/topic, in our case it is "notes"</li>
</ul>
</li>
<li>Question arises where do we place this function. Ideally, it should be placed in the context API layer but we haven't created any context API layer. So, we will create a new module <code>lib/note_app/notes/note.ex</code> and the following code.<pre><code>defmodule NoteApp.Notes.Note <span class="hljs-keyword">do</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subscribe</span><span class="hljs-params">()</span></span> <span class="hljs-keyword">do</span>
     Phoenix.PubSub.subscribe(NoteApp.PubSub, <span class="hljs-string">"notes"</span>)
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre></li>
</ul>
<h3 id="publishingbroadcasting-the-message">Publishing/Broadcasting the message</h3>
<ul>
<li>We know that PubSub has a <code>subscribe</code> function, likewise, it also has a <code>broadcast</code> function in it.</li>
<li>We have to call this function to <code>publish/broadcast</code> the message.</li>
<li>As per our requirement we want to publish the message(note) as soon as we create the note.</li>
<li>So our logic will reside in our <code>create_note</code> callback just after creating the note.</li>
<li>We need to pass the message as a tuple with the first field as the <code>message name</code>(as an atom) in our case it will be <code>:create_note</code> and the other field is the <code>note</code> created.</li>
</ul>
<p><code>lib/note_app/notes/note_server.ex</code></p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_cast</span><span class="hljs-params">({<span class="hljs-symbol">:create_note</span>, note}, notes)</span></span> <span class="hljs-keyword">do</span>
    updated_note = add_id(notes, note)
    updates_notes = [updated_note <span class="hljs-params">| notes]

    message = {:note_created, updated_note}

    #broadcast logic -start
    Phoenix.PubSub.broadcast(
      NoteApp.PubSub,
      "notes",
      message
    )
    #broadcast logic -<span class="hljs-keyword">end</span>
    {:noreply, updates_notes}
  <span class="hljs-keyword">end</span></span>
</code></pre><ul>
<li>The <code>broadcast</code> function takes 3 parameters.<ul>
<li>The PubSub instance of our app, in our case it is <code>NoteApp.PubSub</code></li>
<li>The name of the channel/topic, in our case it is "notes"</li>
<li>The <code>message</code> we want to <code>broadcast</code>.</li>
</ul>
</li>
</ul>
<h3 id="subscribing-to-the-topicchannel">Subscribing to the Topic/Channel</h3>
<ul>
<li>Any LiveView process which needs to be updated has to subscribe to the topic/channel.</li>
<li>We have already created a <code>notes</code> topic.</li>
<li>Our <code>NotesLists</code> LiveView needs to subscribe to this topic/channel.</li>
<li>This is achieved very easily, we just need to add call the <code>subscribe</code> function in the <code>mount</code> callback.</li>
<li>But there is a caveat, we can only call the <code>subscribe</code> function when our LiveView is connected to <code>socket</code>. Also, we need to alias the <code>Note</code> module to use 
<code>lib/note_app_web/live/notes_index_live.ex</code><pre><code>defmodule NoteAppWeb.NotesIndexLive <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">alias</span> NoteApp.Notes.Note
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">mount</span><span class="hljs-params">(_params, _session, socket)</span></span> <span class="hljs-keyword">do</span>
       <span class="hljs-keyword">if</span> connected?(socket), <span class="hljs-symbol">do:</span> Note.subscribe()
       .....
       .....
       .....
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre></li>
<li>We know LiveView is nothing but a GenServer process, so when the publisher broadcasts the message our LiveView process needs to handle that message. We will achieve that with the <code>handle_info</code> callback in our <code>NotesList</code> LiveView module. Add the following function in the module.</li>
</ul>
<p><code>lib/note_app_web/live/notes_index_live.ex</code></p>
<pre><code>  defmodule NoteAppWeb.NotesIndexLive <span class="hljs-keyword">do</span>
        ........
        ........
        ........

       <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_info</span><span class="hljs-params">({<span class="hljs-symbol">:note_created</span>, note}, socket)</span></span> <span class="hljs-keyword">do</span>
           socket = update(socket, <span class="hljs-symbol">:notes</span>, fn notes -&gt; [note <span class="hljs-params">| notes] <span class="hljs-keyword">end</span>)
           {:noreply, socket}
      <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span></span>
</code></pre><ul>
<li>We pattern match the <code>broadcast</code> message and retrieve the <code>note</code> and update the <code>notes</code> list in the socket with the latest message.</li>
</ul>
<p>I hope you like this easy implementation of using Phoenix PubSub for achieving live updates. If you have any questions then please comment below.</p>
<h3 id="references">References:</h3>
<ul>
<li><a target="_blank" href="https://github.com/abulsayyad123/elixir-note-taking-app">Project Repo</a></li>
<li><a target="_blank" href="https://memorynotfound.com/spring-boot-activemq-publish-subscribe-topic-configuration-example/">Cover Image</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Creating Note taking app using LiveView and GenServer - Part 2]]></title><description><![CDATA[This blog is a continuation of a two-part blog. This is going to be part 2. In the previous blog, we've seen the designing of the process layer (Boundary Layer) of the application. We implemented the GenServer and started it with Supervisor. We also ...]]></description><link>https://abulasar.com/creating-note-taking-app-using-liveview-and-genserver-part-2</link><guid isPermaLink="true">https://abulasar.com/creating-note-taking-app-using-liveview-and-genserver-part-2</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Phoenix framework]]></category><category><![CDATA[liveview]]></category><category><![CDATA[genserver]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Wed, 03 Nov 2021 13:05:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746036282536/0ef33c34-cfd0-4244-ab14-7d8afb45560e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog is a continuation of a two-part blog. This is going to be part 2. In the <a target="_blank" href="https://abulasar.com/creating-note-taking-app-using-liveview-and-genserver-part-1">previous</a> blog, we've seen the designing of the process layer (Boundary Layer) of the application. We implemented the GenServer and started it with Supervisor. We also tested the API layer in <code>iex</code> shell and it is working as per our expectations. In this blog, we are going to implement the UI part of the application using Phoenix Liveview.</p>
<h3 id="heading-plan-of-attack">Plan of attack</h3>
<ul>
<li><p>We are going to add different LiveView routes ✓</p>
</li>
<li><p>We will implement List page displaying all notes ✓</p>
</li>
<li><p>Implement detail page for note ✓</p>
</li>
<li><p>Add update and delete action for the note ✓</p>
</li>
<li><p>Add create note form ✓</p>
</li>
<li><p>We will be integrating the API with each of these actions. ✓</p>
</li>
</ul>
<h2 id="heading-lets-start-adding-routes-definition">Let's start adding routes definition 🚧</h2>
<ul>
<li><p>As discussed we are going to have 3 routes.</p>
</li>
<li><p>One for "List", "detail" and one for "create".</p>
</li>
<li><p>For adding route open <code>lib/note_app_web/router.ex</code> file and add following code.</p>
</li>
</ul>
<pre><code class="lang-sql">scope "/", NoteAppWeb <span class="hljs-keyword">do</span>
    pipe_through :browser

    <span class="hljs-comment"># These are the new routes</span>
    live <span class="hljs-string">"/"</span>, NotesIndexLive
    live <span class="hljs-string">"/notes/new"</span>, NotesNewLive
    live <span class="hljs-string">"/notes/:id/edit"</span>, NotesEditLive
  <span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p><code>live</code> is a macro that requires 3 arguments, in this case, we are passing 2.</p>
<ol>
<li><p>Endpoint URL</p>
</li>
<li><p>Module name of the <code>liveview</code> file associated with that endpoint.</p>
</li>
</ol>
</li>
<li><p>So, we are going to have 3 modules in the folder <code>lib/note_app_web/live</code>. They are</p>
<ol>
<li><p><code>notes_index_live.ex</code></p>
</li>
<li><p><code>notes_new_live.ex</code></p>
</li>
<li><p><code>notes_edit_live.ex</code>.</p>
</li>
</ol>
</li>
<li><p>These routes are self-explanatory as per the name.</p>
</li>
</ul>
<h3 id="heading-add-notes-list-page">Add Notes List page ✅</h3>
<ul>
<li><p>Every live module acts like a controller, which is invoked as soon as we hit the route associated with that live file.</p>
</li>
<li><p>Every <code>live</code> file starts with the <code>mount</code> function which acts like a constructor (in OOP sense). It takes 3 parameters</p>
<ul>
<li><p><code>params</code> =&gt;<code>params</code> has URL parameter specific data.</p>
</li>
<li><p><code>session</code> =&gt; User session specific information.</p>
</li>
<li><p><code>socket</code> =&gt; It acts as a <code>state</code> for every Liveview page.</p>
</li>
</ul>
</li>
<li><p>It returns the <code>ok</code> tuple with the <code>socket</code>.</p>
</li>
<li><p>Since, this page is about the list of <code>notes</code>, we are going to query our GenServer about the list of notes it has.</p>
</li>
<li><p>We are going to assign queried <code>list of notes</code> and <code>number of notes</code> to the socket variable.</p>
</li>
</ul>
<pre><code class="lang-sql">defmodule NoteAppWeb.NotesIndexLive <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">use</span> NoteAppWeb, :live_view
    <span class="hljs-keyword">alias</span> NoteApp.Notes.NoteServer

    <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(_params, _session, socket) <span class="hljs-keyword">do</span>
       all_notes = NoteServer.all_notes()
       socket = assign(socket, :notes, all_notes)
       socket = assign(socket, :notes_length, Kernel.length(all_notes))
       {:ok, socket}
    <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p>As you will notice we have <code>alias</code> the <code>NoteServer</code> module i.e GenServer. If you remember there is an <code>all_notes</code> function in it. We have called it and assigned it to the <code>all_notes</code> variable.</p>
</li>
<li><p>Using the <code>assign</code> function we have assigned these <code>notes</code> to the <code>notes</code> variable.</p>
</li>
<li><p>Similarly, we have assigned the number of notes to the <code>notes_length</code> variable.</p>
</li>
<li><p>Now our data is ready, now we need to render the view.</p>
</li>
<li><p>For that Liveview has a <code>render</code> function. We have our list of notes and their count we are going to display it in this render function.</p>
</li>
</ul>
<pre><code class="lang-sql">   ~L"""
      &lt;div class="notes"&gt;
        &lt;div class="notes-header"&gt;
            &lt;h2 class="notes-title"&gt;&amp;<span class="hljs-comment">#9782; Notes&lt;/h2&gt;</span>
            &lt;p class="notes-count"&gt;&lt;%= @notes_length %&gt;&lt;/p&gt;
        &lt;/div&gt;

        &lt;div class="notes-list"&gt;
          &lt;%= for note &lt;- @notes <span class="hljs-keyword">do</span> %&gt;
            &lt;%= live_patch <span class="hljs-keyword">to</span>: Routes.live_path(@socket, NotesEditLive, note.id) <span class="hljs-keyword">do</span> %&gt;
              &lt;<span class="hljs-keyword">div</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"notes-list-item"</span>&gt;
                &lt;h3&gt;&lt;%= note.title %&gt;&lt;/h3&gt;
                &lt;p&gt;&lt;%= note.body %&gt;&lt;/p&gt;
              &lt;/<span class="hljs-keyword">div</span>&gt;
            &lt;% <span class="hljs-keyword">end</span> %&gt;
          &lt;% <span class="hljs-keyword">end</span> %&gt;
        &lt;/<span class="hljs-keyword">div</span>&gt;

        &lt;%= live_patch <span class="hljs-string">"+"</span>, <span class="hljs-keyword">to</span>: Routes.live_path(@socket, NotesNewLive), <span class="hljs-keyword">class</span>: <span class="hljs-string">"floating-button"</span> %&gt;
      &lt;/<span class="hljs-keyword">div</span> &gt;
    <span class="hljs-string">"""</span>
</code></pre>
<ul>
<li><p>This HTML code has lots of styling which you can copy from the <code>assets/app.css</code> file in the project repo of this blog.</p>
</li>
<li><p>Rest displaying the logic of the list of notes is simple, we are just iterating over the <code>@notes</code> variable and displaying it under the <code>notes-list-item</code> div.</p>
</li>
<li><p>There is <code>live_patch</code> <code>helper</code> defined twice in the view. One is used to navigate to the detail page of the particular note. The other navigate to the <code>create</code> page.</p>
</li>
<li><p>This function takes the <code>Route.live_path</code> value which says where we are going to navigate and its associated params.</p>
</li>
<li><p>So we are passing the <code>NotesEditLive</code> and <code>NotesNewLive</code> as value to these helpers.</p>
</li>
</ul>
<h3 id="heading-add-detail-page-for-note-along-with-edit-and-delete-action">Add detail page for note along with edit ✄ and delete ✕ action</h3>
<ul>
<li><p>Detail page is where we are going to display detail of the note title and its body.</p>
</li>
<li><p>We have a separate LiveView file for it as well i.e <code>NotesEditLive</code>.</p>
</li>
<li><p>We are going to add two actions <code>update</code> and <code>delete</code> in the view.</p>
</li>
<li><p>We have these two methods in our <code>NoteServer</code> module as well.</p>
</li>
<li><p>If you remember we added <code>live_patch</code> link to naviate to the details page. We passed <code>Routes.live_path(@socket, NotesEditLive, note.id)</code> to the <code>live_patch</code> helper.</p>
</li>
<li><p>The <code>note.id</code> in the <code>live_path</code> function is nothing but path param for the detail page which generates URL something like this <code>notes/1/edit</code>.</p>
</li>
<li><p>Inside the <code>details</code> LiveView mount function we are going to fetch this <code>id</code> from the <code>params</code> parameter of the mount function.</p>
</li>
<li><p>Using this <code>id</code> we are going to fetch a particular note.</p>
</li>
<li><p>In file <code>lib/note_app_web/live/notes_new_live.ex</code> we are going to add following <code>mount</code> function.</p>
</li>
</ul>
<pre><code class="lang-sql">defmodule NoteAppWeb.NotesEditLive <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">alias</span> NoteAppWeb.NotesIndexLive
    <span class="hljs-keyword">use</span> NoteAppWeb, :live_view
    <span class="hljs-keyword">alias</span> NoteApp.Notes.NoteServer

    <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(%{<span class="hljs-string">"id"</span> =&gt; <span class="hljs-keyword">id</span>}, _session, socket) <span class="hljs-keyword">do</span>
       note = NoteServer.get_note(String.to_integer(<span class="hljs-keyword">id</span>))
      {:ok, assign(socket, :note, note)}
    <span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li><p>Similarly, we are going to add our view in the <code>render</code> function. There we have added the form with some <code>handle_events</code> like <code>phx-change</code> to manage change in the inputs and <code>phx-submit</code> to submit the form. (Check References for these handle_events)</p>
</li>
<li><p>We have added the <code>Delete</code> button with the <code>phx-click</code> event which handles the logic to delete the note.</p>
</li>
</ul>
<pre><code class="lang-sql">      def handle_event("delete_note", _value, socket) <span class="hljs-keyword">do</span>
           NoteServer.delete_note(socket.assigns.note.id)
           {:noreply,
              socket
              |&gt; redirect(<span class="hljs-keyword">to</span>: <span class="hljs-string">"/"</span>)
           }
      <span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li>Finally, we have <code>phx-submit</code> event that gets called on clicking the <code>Done</code> button, and logic for updating the note is called.</li>
</ul>
<pre><code class="lang-sql">     def handle_event("save_note", _value, socket) <span class="hljs-keyword">do</span>
           NoteServer.update_note(socket.assigns.note)
          {:noreply,
             socket
             |&gt; redirect(<span class="hljs-keyword">to</span>: <span class="hljs-string">"/"</span>)
           }
      <span class="hljs-keyword">end</span>
</code></pre>
<ul>
<li>The final working code for the Notes detail Liveview is below.</li>
</ul>
<pre><code class="lang-sql">defmodule NoteAppWeb.NotesEditLive <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">alias</span> NoteAppWeb.NotesIndexLive
    <span class="hljs-keyword">use</span> NoteAppWeb, :live_view
    <span class="hljs-keyword">alias</span> NoteApp.Notes.NoteServer

    <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(%{<span class="hljs-string">"id"</span> =&gt; <span class="hljs-keyword">id</span>}, _session, socket) <span class="hljs-keyword">do</span>
       note = NoteServer.get_note(String.to_integer(<span class="hljs-keyword">id</span>))
      {:ok, assign(socket, :note, note)}
    <span class="hljs-keyword">end</span>

    <span class="hljs-keyword">def</span> render(assigns) <span class="hljs-keyword">do</span>
      ~L<span class="hljs-string">"""
        &lt;div class="</span>note<span class="hljs-string">"&gt;
            &lt;div class="</span>note-header<span class="hljs-string">"&gt;
               &lt;h3&gt;
                    &lt;%= live_patch "</span>&lt;<span class="hljs-string">", to: Routes.live_path(@socket, NotesIndexLive) %&gt;
               &lt;/h3&gt;
               &lt;button type="</span>submit<span class="hljs-string">" form="</span><span class="hljs-keyword">create</span>-note-<span class="hljs-keyword">form</span><span class="hljs-string">"&gt;Done&lt;/button&gt;
               &lt;button phx-click="</span>delete_note<span class="hljs-string">"&gt;Delete&lt;/button&gt;
             &lt;/div&gt;

             &lt;form id="</span><span class="hljs-keyword">create</span>-note-<span class="hljs-keyword">form</span><span class="hljs-string">" phx-change="</span>detect_change<span class="hljs-string">" phx-submit="</span>save_note<span class="hljs-string">"&gt;
                &lt;input type="</span><span class="hljs-built_in">text</span><span class="hljs-string">" placeholder="</span><span class="hljs-keyword">Add</span> Title<span class="hljs-string">" value="</span>&lt;%= @note.title %&gt;<span class="hljs-string">" name="</span>title<span class="hljs-string">"/&gt;
                 &lt;br&gt;
                 &lt;br&gt;
                 &lt;textarea placeholder="</span><span class="hljs-keyword">Add</span> note<span class="hljs-string">" name="</span><span class="hljs-keyword">body</span><span class="hljs-string">"&gt;&lt;%= @note.body %&gt;&lt;/textarea&gt;
             &lt;/form&gt;
          &lt;/div &gt;
       """</span>
      <span class="hljs-keyword">end</span>

      <span class="hljs-keyword">def</span> handle_event(<span class="hljs-string">"detect_change"</span>, %{<span class="hljs-string">"title"</span> =&gt; title, <span class="hljs-string">"body"</span> =&gt; <span class="hljs-keyword">body</span>}, socket) <span class="hljs-keyword">do</span>
          updated_note = %{socket.assigns.note | title: title, <span class="hljs-keyword">body</span>: <span class="hljs-keyword">body</span>}
         {:noreply, assign(socket, :note, updated_note)}
      <span class="hljs-keyword">end</span>

      <span class="hljs-keyword">def</span> handle_event(<span class="hljs-string">"save_note"</span>, _value, socket) <span class="hljs-keyword">do</span>
           NoteServer.update_note(socket.assigns.note)
          {:noreply,
             socket
             |&gt; redirect(<span class="hljs-keyword">to</span>: <span class="hljs-string">"/"</span>)
           }
      <span class="hljs-keyword">end</span>

      <span class="hljs-keyword">def</span> handle_event(<span class="hljs-string">"delete_note"</span>, _value, socket) <span class="hljs-keyword">do</span>
           NoteServer.delete_note(socket.assigns.note.id)
           {:noreply,
              socket
              |&gt; redirect(<span class="hljs-keyword">to</span>: <span class="hljs-string">"/"</span>)
           }
      <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-add-page-for-creating-a-new-note">Add page for creating a new note</h3>
<ul>
<li><p>The only part that is remaining is the creation of <code>note</code>.</p>
</li>
<li><p>The form on the <code>/new</code> endpoint is very similar to the details page.</p>
</li>
<li><p>We are navigating to this page by passing <code>NotesNewLive</code> module to <code>live_path</code>. <code>Routes.live_path(@socket, NotesNewLive)</code></p>
</li>
<li><p>In this LiveView module we are going to initialize empty variables <code>title</code> and <code>body</code> for the form and will use them in the render function.</p>
</li>
<li><p>Similar to the edit page we will have handle_events on saving the form where we are going to call the <code>create_note</code> function from our GenServer API.</p>
</li>
</ul>
<pre><code class="lang-sql">defmodule NoteAppWeb.NotesNewLive <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">use</span> NoteAppWeb, :live_view
  <span class="hljs-keyword">alias</span> NoteApp.Notes.NoteServer
  <span class="hljs-keyword">alias</span> NoteAppWeb.NotesIndexLive

  <span class="hljs-keyword">def</span> <span class="hljs-keyword">mount</span>(_params, _session, socket) <span class="hljs-keyword">do</span>
    socket = assign(socket, title: <span class="hljs-string">""</span>, <span class="hljs-keyword">body</span>: <span class="hljs-string">""</span>)
    {:ok, socket}
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> render(assigns) <span class="hljs-keyword">do</span>
    ~L<span class="hljs-string">"""
    &lt;div class="</span>note<span class="hljs-string">"&gt;
      &lt;div class="</span>note-header<span class="hljs-string">"&gt;
        &lt;h3&gt;
          &lt;%= live_patch "</span>&lt;<span class="hljs-string">", to: Routes.live_path(@socket, NotesIndexLive) %&gt;
        &lt;/h3&gt;
        &lt;button type="</span>submit<span class="hljs-string">" form="</span><span class="hljs-keyword">create</span>-note-<span class="hljs-keyword">form</span><span class="hljs-string">"&gt;Done&lt;/button&gt;
      &lt;/div&gt;

      &lt;form id="</span><span class="hljs-keyword">create</span>-note-<span class="hljs-keyword">form</span><span class="hljs-string">" phx-change="</span>detect_change<span class="hljs-string">" phx-submit="</span>save_note<span class="hljs-string">"&gt;
        &lt;input type="</span><span class="hljs-built_in">text</span><span class="hljs-string">" placeholder="</span><span class="hljs-keyword">Add</span> Title<span class="hljs-string">" value="</span>&lt;%= @title %&gt;<span class="hljs-string">" name="</span>title<span class="hljs-string">"/&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;textarea placeholder="</span><span class="hljs-keyword">Add</span> note<span class="hljs-string">" name="</span><span class="hljs-keyword">body</span><span class="hljs-string">"&gt;&lt;%= @body %&gt;&lt;/textarea&gt;
      &lt;/form&gt;
    &lt;/div &gt;
    """</span>
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> handle_event(<span class="hljs-string">"detect_change"</span>, %{<span class="hljs-string">"title"</span> =&gt; title, <span class="hljs-string">"body"</span> =&gt; <span class="hljs-keyword">body</span>}, socket) <span class="hljs-keyword">do</span>
    {:noreply, assign(socket, title: title, <span class="hljs-keyword">body</span>: <span class="hljs-keyword">body</span>)}
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> handle_event(<span class="hljs-string">"save_note"</span>, _value, socket) <span class="hljs-keyword">do</span>
    new_note = %{<span class="hljs-keyword">id</span>: nil, title: socket.assigns.title, <span class="hljs-keyword">body</span>: socket.assigns.body}
    NoteServer.create_note(new_note)
    {:noreply,
         socket
         |&gt; redirect(<span class="hljs-keyword">to</span>: <span class="hljs-string">"/"</span>)
    }
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>So, that's our complete LiveView implementation of all CRUD functions. There is some scope of refactoring in the app like creating components for common code (which I will cover in another blog 😉). The main purpose of the blog was to introduce the concept of GenServer and LiveView. I hope you like this blog. If you have any questions then please comment below.</p>
<h3 id="heading-references">References:</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/abulsayyad123/elixir-note-taking-app">Project Repo</a></p>
</li>
<li><p><a target="_blank" href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html">LiveView</a></p>
</li>
<li><p><a target="_blank" href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_event/3">handle_events</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>