<?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>Fri, 09 Oct 2026 20:38:43 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[Singular Value Decomposition: From the Math to C++ with Eigen]]></title><description><![CDATA[Introduction
Imagine a cinema recording how many tickets it sells for each film every day. The rows of its table are days, the columns are films, and each number is a ticket count.
Suppose Film A sell]]></description><link>https://abulasar.com/singular-value-decomposition-cpp-eigen</link><guid isPermaLink="true">https://abulasar.com/singular-value-decomposition-cpp-eigen</guid><category><![CDATA[#svd]]></category><category><![CDATA[linear algebra ]]></category><category><![CDATA[C++]]></category><category><![CDATA[eigen]]></category><category><![CDATA[Mathematics]]></category><category><![CDATA[Machine Learning]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Wed, 30 Sep 2026 07:36:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5f632ac61d914f19b97de8b2/a55bded1-7b47-4b85-9c48-1c3d68ac96cb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2>
<p>Imagine a cinema recording how many tickets it sells for each film every day. The rows of its table are days, the columns are films, and each number is a ticket count.</p>
<p>Suppose Film A sells 100 tickets on Friday and 200 on Saturday. Film B sells 200 on Friday and 400 on Saturday. The table contains four numbers, but one simple pattern explains them: Saturday’s sales are twice Friday’s, and Film B’s sales are twice Film A’s.</p>
<p>In a table with thousands of days and films, patterns like this would be harder to spot. <strong>Singular Value Decomposition (SVD)</strong> helps us find them. It describes a matrix as a collection of patterns, ordered by how strongly each one contributes. We can use all the patterns to rebuild the original matrix, or keep only the strongest ones to make an approximation.</p>
<p>For a matrix <strong>A</strong>, SVD is written as</p>
<p>$$A = U\Sigma V^T$$</p>
<p>Think of the three parts as answering three questions:</p>
<ul>
<li><p><strong>U:</strong> How much does each row take part in a pattern? In our example, the rows are days.</p>
</li>
<li><p><strong>Vᵀ:</strong> How much does each column take part in the matching pattern? Here, the columns are films.</p>
</li>
<li><p><strong>Σ:</strong> How strong is that pattern?</p>
</li>
</ul>
<p>The first column of <strong>U</strong>, the first singular value in <strong>Σ</strong>, and the first row of <strong>Vᵀ</strong> work together to describe one pattern. The second column, value, and row describe another. Adding all of them gives us <strong>A</strong>.</p>
<p>This does not mean SVD knows what a “day” or a “film” is. It works with the numbers in the matrix. The ticket table is simply a way to understand what its three parts do.</p>
<p>Eigenvalue decomposition helps us study certain directions of a square matrix. SVD gives us a related way to study <strong>any real matrix</strong>, including a rectangular table with a different number of rows and columns.</p>
<p>If</p>
<p>$$A \in \mathbb{R}^{m \times n},$$</p>
<p>then the matrices in the <strong>full SVD</strong> have these dimensions:</p>
<p>$$U \in \mathbb{R}^{m \times m}, \qquad \Sigma \in \mathbb{R}^{m \times n}, \qquad V \in \mathbb{R}^{n \times n}.$$</p>
<p>The columns of <strong>U</strong> and <strong>V</strong> are called <em>left</em> and <em>right singular vectors</em>. The nonnegative numbers on the diagonal of <strong>Σ</strong> are called <em>singular values</em>. Both <strong>U</strong> and <strong>V</strong> are orthogonal matrices:</p>
<p>$$U^TU = I_m, \qquad V^TV = I_n.$$</p>
<p>In the rest of this article, we will calculate these three matrices step by step, then implement the same calculation in C++ using Eigen.</p>
<h2>Finding the Singular Value Decomposition</h2>
<p>Let</p>
<p>$$A \in \mathbb{R}^{m \times n}.$$</p>
<p>We will begin with the case <strong>m ≥ n</strong>, where <strong>A</strong> has at least as many rows as columns.</p>
<h3>Form a Square Symmetric Matrix</h3>
<p>Calculate</p>
<p>$$M = A^TA.$$</p>
<p>Because <strong>A</strong> is an <strong>m × n</strong> matrix,</p>
<p>$$A^TA \in \mathbb{R}^{n \times n}.$$</p>
<p>The matrix <strong>AᵀA</strong> is square, symmetric, and positive semidefinite. To see why its eigenvalues cannot be negative, take any vector <strong>x</strong>:</p>
<p>$$x^T(A^TA)x=(Ax)^T(Ax)=\left|Ax\right|_2^2\geq0$$</p>
<p>The eigenvectors of <strong>AᵀA</strong> will give us the columns of <strong>V</strong>, while its eigenvalues will give us the squared singular values.</p>
<p><em>Note: We find the eigenvalues of AᵀA, not the eigenvalues of A itself.</em></p>
<h3>Find the Eigenvalues</h3>
<p>Solve the characteristic equation</p>
<p>$$\det(A^TA - \lambda I) = 0.$$</p>
<p>For <strong>m ≥ n</strong>, this gives <strong>n</strong> eigenvalues. Arrange them from largest to smallest:</p>
<p>$$\lambda_1 \ge \lambda_2 \ge \cdots \ge \lambda_n \ge 0.$$</p>
<p>The eigenvalues may be repeated, and some of them may be zero.</p>
<h3>Calculate the Singular Values</h3>
<p>The singular values are the square roots of the eigenvalues:</p>
<p>$$\sigma_i = \sqrt{\lambda_i}.$$</p>
<p>Therefore,</p>
<p>$$\sigma_1 \ge \sigma_2 \ge \cdots \ge \sigma_n \ge 0.$$</p>
<p>The number of positive singular values is equal to the rank of <strong>A</strong>.</p>
<p><em>Note: Singular values are conventionally written in decreasing order, not increasing order.</em></p>
<h3>Find the Right Singular Vectors</h3>
<p>For each eigenvalue <strong>λᵢ</strong>, solve</p>
<p>$$(A^TA - \lambda_i I)v_i = 0.$$</p>
<p>Normalize the resulting eigenvector:</p>
<p>$$v_i\leftarrow\frac{v_i}{\left|v_i\right|_2}$$</p>
<p>Place the normalized eigenvectors into the columns of <strong>V</strong>:</p>
<p>$$V = \begin{bmatrix}v_1 &amp; v_2 &amp; \cdots &amp; v_n\end{bmatrix}.$$</p>
<p>The order must remain consistent: <strong>vᵢ</strong> must correspond to <strong>λᵢ</strong> and <strong>σᵢ</strong>.</p>
<p>The columns of <strong>V</strong> must be orthonormal:</p>
<p>$$V^TV = I_n.$$</p>
<p>If an eigenvalue is repeated, choose an orthonormal basis for its eigenspace.</p>
<h3>Construct the Sigma Matrix</h3>
<p>The matrix <strong>Σ</strong> has the same dimensions as <strong>A</strong>:</p>
<p>$$\Sigma \in \mathbb{R}^{m \times n}.$$</p>
<p>Place the singular values on its main diagonal in decreasing order and set every other entry to zero.</p>
<p>For a <strong>3 × 2</strong> matrix,</p>
<p>$$\Sigma = \begin{bmatrix} \sigma_1 &amp; 0 \\ 0 &amp; \sigma_2 \\ 0 &amp; 0 \end{bmatrix}.$$</p>
<h3>Find the Left Singular Vectors</h3>
<p>For each nonzero singular value, calculate</p>
<p>$$u_i = \frac{Av_i}{\sigma_i}.$$</p>
<p>Because <strong>σᵢ = √λᵢ</strong>, the same formula can be written as</p>
<p>$$u_i = \frac{Av_i}{\sqrt{\lambda_i}}.$$</p>
<p>Each <strong>uᵢ</strong> must use the matching <strong>vᵢ</strong> and <strong>σᵢ</strong>.</p>
<p>For the full SVD, <strong>U</strong> must contain <strong>m</strong> orthonormal columns. If the formula above does not produce all <strong>m</strong> columns, complete <strong>U</strong> with additional orthonormal vectors.</p>
<p>For a <strong>3 × 2</strong> full-rank matrix, the first two columns are obtained from the formula, and a third column can be found using</p>
<p>$$u_3 = u_1 \times u_2.$$</p>
<p>Normalize <strong>u₃</strong> if necessary. The cross product works only in three-dimensional space. In higher dimensions, use a method such as Gram–Schmidt orthogonalization or find an orthonormal basis for the null space of <strong>Aᵀ</strong>.</p>
<p>Finally,</p>
<p>$$U = \begin{bmatrix}u_1 &amp; u_2 &amp; \cdots &amp; u_m\end{bmatrix},$$</p>
<p>with</p>
<p>$$U^TU = I_m.$$</p>
<p><em>Important: If σᵢ = 0, do not calculate Avᵢ/σᵢ because that would require division by zero. Complete the missing columns of U using an orthonormal basis instead.</em></p>
<h3>Assemble and Verify the Decomposition</h3>
<p>The final decomposition is</p>
<p>$$A = U\Sigma V^T.$$</p>
<p>Notice that the final factor is <strong>Vᵀ</strong>, not <strong>V</strong>.</p>
<p>Verify all three conditions:</p>
<p>$$U^TU = I_m,$$</p>
<p>$$V^TV = I_n,$$</p>
<p>and</p>
<p>$$U\Sigma V^T = A.$$</p>
<p>Small differences are normal when floating-point values or rounded decimal values are used.</p>
<h2>Worked Example</h2>
<p>We will calculate the SVD of the same matrix that we later use in the C++ program:</p>
<p>$$A = \begin{bmatrix} 3 &amp; 1 \\ 2 &amp; 2 \\ 1 &amp; 3 \end{bmatrix}.$$</p>
<p>Here, <strong>m = 3</strong> and <strong>n = 2</strong>, so the full SVD has the dimensions</p>
<p>$$U_{3 \times 3}, \qquad \Sigma_{3 \times 2}, \qquad V_{2 \times 2}.$$</p>
<h3>Calculate AᵀA</h3>
<p>First,</p>
<p>$$A^T = \begin{bmatrix} 3 &amp; 2 &amp; 1 \\ 1 &amp; 2 &amp; 3 \end{bmatrix}.$$</p>
<p>Therefore,</p>
<p>$$A^TA = \begin{bmatrix} 3 &amp; 2 &amp; 1 \\ 1 &amp; 2 &amp; 3 \end{bmatrix} \begin{bmatrix} 3 &amp; 1 \\ 2 &amp; 2 \\ 1 &amp; 3 \end{bmatrix} = \begin{bmatrix} 14 &amp; 10 \\ 10 &amp; 14 \end{bmatrix}.$$</p>
<h3>Find the Eigenvalues</h3>
<p>Solve</p>
<p>$$\det(A^TA - \lambda I) = 0.$$</p>
<p>For this matrix,</p>
<p>$$\begin{aligned} \det \begin{bmatrix} 14-\lambda &amp; 10 \\ 10 &amp; 14-\lambda \end{bmatrix} &amp;= (14-\lambda)^2 - 100 \\ &amp;= \lambda^2 - 28\lambda + 96 \\ &amp;= (\lambda-24)(\lambda-4). \end{aligned}$$</p>
<p>Thus, in decreasing order,</p>
<p>$$\lambda_1 = 24, \qquad \lambda_2 = 4.$$</p>
<h3>Find the Right Singular Vectors</h3>
<p>For <strong>λ₁ = 24</strong>, solving</p>
<p>$$(A^TA-24I)v_1=0$$</p>
<p>gives <strong>x = y</strong>. After normalization, choose</p>
<p>$$v_1 = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 1 \end{bmatrix}.$$</p>
<p>For <strong>λ₂ = 4</strong>, solving</p>
<p>$$(A^TA-4I)v_2=0$$</p>
<p>gives <strong>x = −y</strong>. After normalization, choose</p>
<p>$$v_2 = \frac{1}{\sqrt{2}} \begin{bmatrix} -1 \\ 1 \end{bmatrix}.$$</p>
<p>Therefore,</p>
<p>$$V = \begin{bmatrix}v_1 &amp; v_2\end{bmatrix} = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 &amp; -1 \\ 1 &amp; 1 \end{bmatrix}.$$</p>
<h3>Calculate the Singular Values and Construct Σ</h3>
<p>The singular values are</p>
<p>$$\sigma_1 = \sqrt{24} = 2\sqrt{6} \approx 4.89898,$$</p>
<p>$$\sigma_2 = \sqrt{4} = 2.$$</p>
<p>Thus,</p>
<p>$$\Sigma = \begin{bmatrix} 2\sqrt{6} &amp; 0 \\ 0 &amp; 2 \\ 0 &amp; 0 \end{bmatrix}.$$</p>
<h3>Find the Left Singular Vectors</h3>
<p>Calculate <strong>u₁</strong> using the matching pair <strong>v₁</strong> and <strong>σ₁</strong>:</p>
<p>$$\begin{aligned} u_1 &amp;= \frac{Av_1}{\sigma_1} \\ &amp;= \frac{1}{2\sqrt{6}} \begin{bmatrix} 3 &amp; 1 \\ 2 &amp; 2 \\ 1 &amp; 3 \end{bmatrix} \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 1 \end{bmatrix} \\ &amp;= \frac{1}{\sqrt{3}} \begin{bmatrix} 1 \\ 1 \\ 1 \end{bmatrix}. \end{aligned}$$</p>
<p>Next,</p>
<p>$$\begin{aligned} u_2 &amp;= \frac{Av_2}{\sigma_2} \\ &amp;= \frac{1}{2} \begin{bmatrix} 3 &amp; 1 \\ 2 &amp; 2 \\ 1 &amp; 3 \end{bmatrix} \frac{1}{\sqrt{2}} \begin{bmatrix} -1 \\ 1 \end{bmatrix} \\ &amp;= \frac{1}{\sqrt{2}} \begin{bmatrix} -1 \\ 0 \\ 1 \end{bmatrix}. \end{aligned}$$</p>
<p>To complete the full <strong>3 × 3</strong> matrix <strong>U</strong>, calculate</p>
<p>$$u_3 = u_1 \times u_2 = \frac{1}{\sqrt{6}} \begin{bmatrix} 1 \\ -2 \\ 1 \end{bmatrix}.$$</p>
<p>Therefore,</p>
<p>$$U = \begin{bmatrix} \frac{1}{\sqrt{3}} &amp; -\frac{1}{\sqrt{2}} &amp; \frac{1}{\sqrt{6}} \\ \frac{1}{\sqrt{3}} &amp; 0 &amp; -\frac{2}{\sqrt{6}} \\ \frac{1}{\sqrt{3}} &amp; \frac{1}{\sqrt{2}} &amp; \frac{1}{\sqrt{6}} \end{bmatrix}.$$</p>
<h3>Write the Final SVD</h3>
<p>The decomposition is</p>
<p>$$A = U\Sigma V^T,$$</p>
<p>where</p>
<p>$$V^T = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 &amp; 1 \\ -1 &amp; 1 \end{bmatrix}.$$</p>
<p>Multiplying the factors reconstructs the original matrix:</p>
<p>$$U\Sigma V^T = \begin{bmatrix} 3 &amp; 1 \\ 2 &amp; 2 \\ 1 &amp; 3 \end{bmatrix} = A.$$</p>
<h3>What the Two Patterns Mean</h3>
<p>We can also write the SVD as the sum of two simpler matrices:</p>
<p>$$A=\sigma_1u_1v_1^T+\sigma_2u_2v_2^T =\begin{bmatrix}2&amp;2\\2&amp;2\\2&amp;2\end{bmatrix} +\begin{bmatrix}1&amp;-1\\0&amp;0\\-1&amp;1\end{bmatrix}.$$</p>
<p>Imagine the rows are three days and the columns are two films, with ticket counts measured in hundreds. The first part gives us a simple starting point: <strong>200 tickets for each film on each day</strong>.</p>
<p>The second part adjusts those numbers. On the first day, Film A gets 100 more tickets and Film B gets 100 fewer. On the second day, nothing changes. On the third day, the adjustment is reversed. Adding both parts gives us the exact original table.</p>
<p>If we keep only the first part, we get a simpler approximation in which every value is 2. This is what it means to keep the strongest SVD pattern and leave out the smaller one.</p>
<h3>Why Software May Display Different Signs</h3>
<p>An eigenvector can be multiplied by <strong>−1</strong> and still be an eigenvector. Consequently, singular vectors are not unique in sign.</p>
<p>For any singular-vector pair, replacing both vectors by their negatives does not change the decomposition:</p>
<p>$$(-u_i)\sigma_i(-v_i)^T = u_i\sigma_i v_i^T.$$</p>
<p>This is why our manual result and Eigen's <code>JacobiSVD</code> result may show opposite signs in corresponding columns of <strong>U</strong> and <strong>V</strong>. Both results are correct if they reconstruct <strong>A</strong>.</p>
<h2>What If the Matrix Has More Columns Than Rows?</h2>
<p>When <strong>m &lt; n</strong>, the matrix <strong>AAᵀ</strong> is smaller than <strong>AᵀA</strong>, so it is often more convenient to start with</p>
<p>$$M = AA^T.$$</p>
<p>Its eigenvectors give the left singular vectors directly:</p>
<p>$$AA^T u_i = \lambda_i u_i.$$</p>
<p>The singular values are still</p>
<p>$$\sigma_i = \sqrt{\lambda_i}.$$</p>
<p>For each nonzero singular value, obtain the matching right singular vector from</p>
<p>$$v_i = \frac{A^T u_i}{\sigma_i}.$$</p>
<p>Then complete <strong>V</strong> with additional orthonormal vectors if the full SVD is required.</p>
<p>In summary:</p>
<ul>
<li><p>Starting with <strong>AᵀA</strong> gives <strong>V</strong> first; then calculate <strong>U</strong>.</p>
</li>
<li><p>Starting with <strong>AAᵀ</strong> gives <strong>U</strong> first; then calculate <strong>V</strong>.</p>
</li>
</ul>
<p>Both approaches are mathematically valid. Choosing the smaller square matrix merely reduces the amount of work. The nonzero eigenvalues of <strong>AᵀA</strong> and <strong>AAᵀ</strong> are the same.</p>
<h2>Full SVD and Compact SVD</h2>
<p>The full SVD is</p>
<p>$$A_{m \times n} = U_{m \times m} \Sigma_{m \times n} V^T_{n \times n}.$$</p>
<p>If <strong>A</strong> has rank <strong>r</strong>, keeping only the singular vectors associated with its positive singular values gives the compact SVD:</p>
<p>$$A = U_r\Sigma_rV_r^T,$$</p>
<p>where</p>
<p>$$U_r \in \mathbb{R}^{m \times r}, \qquad \Sigma_r \in \mathbb{R}^{r \times r}, \qquad V_r \in \mathbb{R}^{n \times r}.$$</p>
<p>The compact SVD reconstructs <strong>A</strong> exactly while omitting directions associated with zero singular values.</p>
<h2>Short Formula Sheet</h2>
<p>When starting with <strong>AᵀA</strong>:</p>
<p>$$\boxed{A^TA v_i = \sigma_i^2 v_i}$$</p>
<p>$$\boxed{u_i = \frac{Av_i}{\sigma_i}}$$</p>
<p>When starting with <strong>AAᵀ</strong>:</p>
<p>$$\boxed{AA^T u_i = \sigma_i^2 u_i}$$</p>
<p>$$\boxed{v_i = \frac{A^T u_i}{\sigma_i}}$$</p>
<p>In both cases:</p>
<p>$$\boxed{\sigma_i = \sqrt{\lambda_i}}$$</p>
<p>Finally:</p>
<p>$$\boxed{A = U\Sigma V^T}$$</p>
<h2>C++ Implementation with Eigen</h2>
<p>The C++ program below deliberately performs the mathematical construction first. It then calls Eigen's built-in <code>JacobiSVD</code> implementation so that we can compare the results.</p>
<h3>Project Setup on macOS</h3>
<p>Install CMake and Eigen with Homebrew:</p>
<pre><code class="language-bash">brew install cmake eigen
</code></pre>
<p>Use this project structure:</p>
<pre><code class="language-text">svd-learning/
├── CMakeLists.txt
└── src/
    └── main.cpp
</code></pre>
<p>Create <code>CMakeLists.txt</code>:</p>
<pre><code class="language-cmake">cmake_minimum_required(VERSION 3.20)

project(svd_learning LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(Eigen3 3.3 REQUIRED NO_MODULE)

add_executable(svd_learning src/main.cpp)
target_link_libraries(svd_learning PRIVATE Eigen3::Eigen)
</code></pre>
<p>Configure and build the project:</p>
<pre><code class="language-bash">cmake -S . -B build \
  -DCMAKE_PREFIX_PATH="$(brew --prefix eigen)"

cmake --build build
</code></pre>
<p>Run it:</p>
<pre><code class="language-bash">./build/svd_learning
</code></pre>
<h3>How the Mathematics Maps to Eigen</h3>
<table>
<thead>
<tr>
<th>Mathematical step</th>
<th>Eigen expression</th>
</tr>
</thead>
<tbody><tr>
<td>Define <strong>A</strong></td>
<td><code>Eigen::Matrix&lt;double, 3, 2&gt; A</code></td>
</tr>
<tr>
<td>Calculate <strong>AᵀA</strong></td>
<td><code>A.transpose() * A</code></td>
</tr>
<tr>
<td>Find eigenpairs of <strong>AᵀA</strong></td>
<td><code>Eigen::SelfAdjointEigenSolver</code></td>
</tr>
<tr>
<td>Build <strong>V</strong></td>
<td>Place the ordered eigenvectors into its columns</td>
</tr>
<tr>
<td>Calculate <strong>σᵢ</strong></td>
<td><code>std::sqrt(lambda(i))</code></td>
</tr>
<tr>
<td>Calculate <strong>uᵢ</strong></td>
<td><code>(A * V.col(i)) / sigma(i)</code></td>
</tr>
<tr>
<td>Complete <strong>U</strong> in <strong>R³</strong></td>
<td><code>u1.cross(u2)</code></td>
</tr>
<tr>
<td>Verify the factorization</td>
<td><code>(A - U * Sigma * V.transpose()).norm()</code></td>
</tr>
<tr>
<td>Compute SVD directly</td>
<td><code>Eigen::JacobiSVD</code></td>
</tr>
</tbody></table>
<p><code>SelfAdjointEigenSolver</code> returns eigenvalues in increasing order, so the program reverses the two eigenpairs before constructing <strong>V</strong> and <strong>Σ</strong>.</p>
<h3>Complete <code>main.cpp</code></h3>
<pre><code class="language-cpp">#include &lt;Eigen/Dense&gt;
#include &lt;Eigen/Eigenvalues&gt;
#include &lt;Eigen/Geometry&gt;
#include &lt;Eigen/SVD&gt;

#include &lt;cmath&gt;
#include &lt;iostream&gt;

int main()
{
    // A is a 3 x 2 matrix.
    Eigen::Matrix&lt;double, 3, 2&gt; A;

    A &lt;&lt; 3, 1,
         2, 2,
         1, 3;

    std::cout &lt;&lt; "Matrix A:\n" &lt;&lt; A &lt;&lt; '\n';

    // Step 1: Form A^T A.
    const Eigen::Matrix2d A_transpose_A = A.transpose() * A;

    std::cout &lt;&lt; "\nA^T * A:\n" &lt;&lt; A_transpose_A &lt;&lt; '\n';

    // Step 2: Find the eigenvalues and normalized eigenvectors.
    const Eigen::SelfAdjointEigenSolver&lt;Eigen::Matrix2d&gt; eigen_solver(
        A_transpose_A
    );

    if (eigen_solver.info() != Eigen::Success)
    {
        std::cerr &lt;&lt; "Eigenvalue decomposition failed.\n";
        return 1;
    }

    const Eigen::Vector2d eigenvalues_ascending =
        eigen_solver.eigenvalues();

    const Eigen::Matrix2d eigenvectors_ascending =
        eigen_solver.eigenvectors();

    std::cout &lt;&lt; "\nEigenvalues returned by Eigen:\n"
              &lt;&lt; eigenvalues_ascending.transpose() &lt;&lt; '\n';

    std::cout &lt;&lt; "\nCorresponding normalized eigenvectors:\n"
              &lt;&lt; eigenvectors_ascending &lt;&lt; '\n';

    // Eigen returns the eigenvalues in increasing order.
    // Reorder both eigenvalues and matching eigenvectors for SVD.
    Eigen::Vector2d lambda;
    lambda &lt;&lt; eigenvalues_ascending(1),
              eigenvalues_ascending(0);

    Eigen::Matrix2d V;
    V.col(0) = eigenvectors_ascending.col(1);
    V.col(1) = eigenvectors_ascending.col(0);

    std::cout &lt;&lt; "\nEigenvalues in decreasing SVD order:\n"
              &lt;&lt; lambda.transpose() &lt;&lt; '\n';

    std::cout &lt;&lt; "\nMatrix V:\n" &lt;&lt; V &lt;&lt; '\n';

    std::cout &lt;&lt; "\nV^T * V:\n"
              &lt;&lt; V.transpose() * V &lt;&lt; '\n';

    // Step 3: Singular values are square roots of eigenvalues.
    Eigen::Vector2d sigma;
    sigma &lt;&lt; std::sqrt(lambda(0)),
             std::sqrt(lambda(1));

    std::cout &lt;&lt; "\nSingular values:\n"
              &lt;&lt; sigma.transpose() &lt;&lt; '\n';

    // Step 4: Construct the 3 x 2 Sigma matrix.
    Eigen::Matrix&lt;double, 3, 2&gt; Sigma =
        Eigen::Matrix&lt;double, 3, 2&gt;::Zero();

    Sigma(0, 0) = sigma(0);
    Sigma(1, 1) = sigma(1);

    std::cout &lt;&lt; "\nMatrix Sigma:\n" &lt;&lt; Sigma &lt;&lt; '\n';

    // Step 5: Calculate the first two columns of U.
    const Eigen::Vector3d u1 = (A * V.col(0)) / sigma(0);
    const Eigen::Vector3d u2 = (A * V.col(1)) / sigma(1);

    std::cout &lt;&lt; "\nVector u1:\n" &lt;&lt; u1 &lt;&lt; '\n';
    std::cout &lt;&lt; "\nVector u2:\n" &lt;&lt; u2 &lt;&lt; '\n';

    std::cout &lt;&lt; "\nNorm of u1: " &lt;&lt; u1.norm() &lt;&lt; '\n';
    std::cout &lt;&lt; "Norm of u2: " &lt;&lt; u2.norm() &lt;&lt; '\n';
    std::cout &lt;&lt; "Dot product u1^T * u2: " &lt;&lt; u1.dot(u2) &lt;&lt; '\n';

    // Complete U. The cross product is specific to three dimensions.
    Eigen::Vector3d u3 = u1.cross(u2);
    u3.normalize();

    Eigen::Matrix3d U;
    U.col(0) = u1;
    U.col(1) = u2;
    U.col(2) = u3;

    std::cout &lt;&lt; "\nVector u3:\n" &lt;&lt; u3 &lt;&lt; '\n';
    std::cout &lt;&lt; "\nMatrix U:\n" &lt;&lt; U &lt;&lt; '\n';

    std::cout &lt;&lt; "\nU^T * U:\n"
              &lt;&lt; U.transpose() * U &lt;&lt; '\n';

    // Step 6: Reconstruct A and measure the numerical error.
    const Eigen::Matrix&lt;double, 3, 2&gt; A_reconstructed =
        U * Sigma * V.transpose();

    std::cout &lt;&lt; "\nV^T:\n" &lt;&lt; V.transpose() &lt;&lt; '\n';

    std::cout &lt;&lt; "\nReconstructed A = U * Sigma * V^T:\n"
              &lt;&lt; A_reconstructed &lt;&lt; '\n';

    const double reconstruction_error =
        (A - A_reconstructed).norm();

    constexpr double tolerance = 1e-10;

    std::cout &lt;&lt; "\nReconstruction error: "
              &lt;&lt; reconstruction_error &lt;&lt; '\n';

    std::cout &lt;&lt; "Reconstruction valid: "
              &lt;&lt; std::boolalpha
              &lt;&lt; (reconstruction_error &lt; tolerance)
              &lt;&lt; '\n';

    // Step 7: Compare with Eigen's direct SVD implementation.
    const Eigen::JacobiSVD&lt;Eigen::Matrix&lt;double, 3, 2&gt;&gt; jacobi_svd(
        A,
        Eigen::ComputeFullU | Eigen::ComputeFullV
    );

    if (jacobi_svd.info() != Eigen::Success)
    {
        std::cerr &lt;&lt; "JacobiSVD computation failed.\n";
        return 1;
    }

    const Eigen::Matrix3d U_eigen = jacobi_svd.matrixU();
    const Eigen::Vector2d sigma_eigen = jacobi_svd.singularValues();
    const Eigen::Matrix2d V_eigen = jacobi_svd.matrixV();

    Eigen::Matrix&lt;double, 3, 2&gt; Sigma_eigen =
        Eigen::Matrix&lt;double, 3, 2&gt;::Zero();

    Sigma_eigen.diagonal() = sigma_eigen;

    std::cout &lt;&lt; "\n--- Eigen JacobiSVD result ---\n";

    std::cout &lt;&lt; "\nEigen singular values:\n"
              &lt;&lt; sigma_eigen.transpose() &lt;&lt; '\n';

    std::cout &lt;&lt; "\nEigen matrix U:\n" &lt;&lt; U_eigen &lt;&lt; '\n';
    std::cout &lt;&lt; "\nEigen matrix Sigma:\n" &lt;&lt; Sigma_eigen &lt;&lt; '\n';
    std::cout &lt;&lt; "\nEigen matrix V:\n" &lt;&lt; V_eigen &lt;&lt; '\n';

    const Eigen::Matrix&lt;double, 3, 2&gt; A_reconstructed_eigen =
        U_eigen * Sigma_eigen * V_eigen.transpose();

    const double eigen_reconstruction_error =
        (A - A_reconstructed_eigen).norm();

    std::cout &lt;&lt; "\nA reconstructed using JacobiSVD:\n"
              &lt;&lt; A_reconstructed_eigen &lt;&lt; '\n';

    std::cout &lt;&lt; "\nJacobiSVD reconstruction error: "
              &lt;&lt; eigen_reconstruction_error &lt;&lt; '\n';

    std::cout &lt;&lt; "Singular values match: "
              &lt;&lt; sigma.isApprox(sigma_eigen, tolerance)
              &lt;&lt; '\n';

    return 0;
}
</code></pre>
<h3>Expected Key Results</h3>
<p>The program should report</p>
<pre><code class="language-text">Eigenvalues in decreasing SVD order:
24 4

Singular values:
4.89898 2

Reconstructed A = U * Sigma * V^T:
3 1
2 2
1 3

Reconstruction valid: true
Singular values match: true
</code></pre>
<h2>Conclusion</h2>
<p>The central relationships behind SVD are</p>
<p>$$A^TA v_i = \sigma_i^2 v_i,$$</p>
<p>$$Av_i = \sigma_i u_i,$$</p>
<p>and</p>
<p>$$A = U\Sigma V^T.$$</p>
<p>In this example, we derived every matrix by hand, reconstructed the original matrix in C++, and confirmed the result using Eigen's built-in <code>JacobiSVD</code> implementation.</p>
<p>If you have a question or notice something that could be explained more clearly, please leave a comment. Thanks for reading! 😊</p>
]]></content:encoded></item><item><title><![CDATA[Debugging a False-Positive Hex Advisory in an Elixir CI Pipeline]]></title><description><![CDATA[Introduction
Recently, while working on a personal project in Elixir/Phoenix, I was performing what looked like a routine dependency update in an Elixir/Phoenix project:
mix deps.update mint

The comm]]></description><link>https://abulasar.com/debugging-false-positive-hex-advisory-elixir-ci</link><guid isPermaLink="true">https://abulasar.com/debugging-false-positive-hex-advisory-elixir-ci</guid><category><![CDATA[Elixir]]></category><category><![CDATA[Erlang]]></category><category><![CDATA[vulnerability]]></category><category><![CDATA[mix]]></category><category><![CDATA[hex ]]></category><category><![CDATA[audit]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sat, 19 Sep 2026 17:45:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5f632ac61d914f19b97de8b2/25f45e07-68d1-44c1-827a-c3d02dc51c08.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Introduction</h3>
<p>Recently, while working on a personal project in Elixir/Phoenix, I was performing what looked like a routine dependency update in an Elixir/Phoenix project:</p>
<pre><code class="language-shell">mix deps.update mint
</code></pre>
<p>The command was about Mint, but Hex surfaced a security advisory for a different package: Decimal 3.1.1.</p>
<p>My CI pipeline then ran its dependency audit:</p>
<pre><code class="language-shell">mix hex.audit
</code></pre>
<p>The audit reported <code>CVE-2026-32686</code> against Decimal 3.1.1 and returned a non-zero exit code. CI stopped.</p>
<p>At first, the obvious interpretation was that my project contained a vulnerable Decimal release. That interpretation turned out to be wrong, but CI was still right to stop it.</p>
<h3>Two commands, two different responsibilities</h3>
<p>The first detail I had to understand was that <code>mix deps.update mint</code> and <code>mix hex.audit</code> were doing different jobs.</p>
<p>Mix documents that a targeted update unlocks the named dependency and the projects it depends on. But since Hex 2.5, dependency resolution commands also display advisories associated with resolved packages. Those warnings are informational; they do not fail the command by default.</p>
<p><code>mix hex.audit</code> is different. It examines the complete lockfile and exits with a non-zero status when a locked package has an active advisory. Hex explicitly describes it as suitable for CI. <a href="https://hex.pm/blog/hex-v25-released">Hex 2.5 announcement</a>.</p>
<p>That means the Mint update did not necessarily introduce the Decimal problem. It merely caused Hex to inspect the resolved dependency set and surface the advisory. The audit then independently evaluated the complete lockfile and enforced my CI policy.</p>
<p>One repository-specific detail still needs the original lockfile diff to determine whether the Mint update changed Decimal at all, or whether Decimal 3.1.1 was already present. I do not want to invent that part of the story.</p>
<h3>Was Decimal 3.1.1 actually vulnerable?</h3>
<p>No.</p>
<p>The vulnerability itself was real. Older Decimal versions accepted numbers with enormous exponents. An untrusted decimal with an extremely large exponent could later cause arithmetic, formatting, rounding, or integer conversion to allocate huge amounts of memory. In an exposed application, that could exhaust memory and crash the BEAM VM.</p>
<p>The authoritative affected range is:</p>
<pre><code class="language-text">Decimal &gt;= 0.1.0 and &lt; 3.0.0
Fixed in Decimal 3.0.0
</code></pre>
<p>Decimal 3.0.0 introduced bounded defaults based on IEEE 754 decimal128 limits. The change is documented in the <a href="https://github.com/ericmj/decimal/releases/tag/v3.0.0">3.0.0 release notes</a> and its <a href="https://github.com/ericmj/decimal/commit/6a523f3a73b8c9974540e21c7aa88f1258bb35ae">fix commit</a>.</p>
<p>My installed version, 3.1.1, was newer than the fixed boundary. The current <a href="https://cna.erlef.org/cves/CVE-2026-32686.html">EEF CNA record</a>, <a href="https://osv.dev/vulnerability/EEF-CVE-2026-32686">OSV entry</a>, and <a href="https://github.com/ericmj/decimal/security/advisories/GHSA-rhv4-8758-jx7v">GitHub advisory</a> all classify versions from 3.0.0 onward as unaffected.</p>
<p>So why did the scanner report 3.1.1?</p>
<h3>A machine-readable contradiction</h3>
<p>Security scanners usually do not inspect a dependency’s source code and independently prove that a vulnerability exists. They consume structured advisory data.</p>
<p>In this case, the data travelled roughly like this:</p>
<ol>
<li><p>The vulnerability was published as a CVE and GitHub security advisory.</p>
</li>
<li><p>The Erlang Ecosystem Foundation published a machine-readable record to OSV.</p>
</li>
<li><p>Hex imported the OSV data and associated its affected ranges with package releases.</p>
</li>
<li><p>The Hex client compared those releases with my <code>mix.lock</code>.</p>
</li>
<li><p><code>mix hex.audit</code> returned a failure when it found a match.</p>
</li>
</ol>
<p>The original CVE record contained two conflicting ideas.</p>
<p>It declared an explicit vulnerable range ending before 3.0.0, but it also set the record’s <code>defaultStatus</code> to <code>affected</code>. In machine-readable terms, that default could mean that every version outside the explicitly described range was affected too, including 3.1.1.</p>
<p>According to the EEF maintainer’s <a href="https://elixirforum.com/t/security-advisory-decimal-dos-vulnerability/75261">explanation of the incident</a>, the error had previously remained hidden because their tooling did not support <code>defaultStatus</code>. A newer tooling release began honoring the field and consequently classified every version as affected.</p>
<p>The scanner’s result was wrong for my installed version, but it had not randomly invented the result. It had faithfully processed incorrect metadata.</p>
<p>On September 8, 2026, the record was corrected from <code>text defaultStatus: affected</code> to <code>defaultStatus: unaffected</code>. The <a href="https://github.com/CVEProject/cvelistV5/commit/893f0ee89e0bf4e8d8e7330b5e30a11f4a3c75d3">public correction diff</a> records that change.</p>
<h3>Why I did not bypass CI immediately</h3>
<p>A false positive is frustrating, especially when the installed version appears newer than the published fix. But CI did not have enough context to make that judgment.</p>
<p>Its job was simple. If a locked dependency was identified as vulnerable, stop and require investigation.</p>
<p>That is exactly what happened.</p>
<p>If the pipeline had silently continued whenever an advisory looked inconvenient, a future genuine vulnerability could have crossed the same boundary. Security checks are useful partly because they interrupt normal work and force an explicit decision.</p>
<p>The correct response was not “the scanner must be right,” but it was also not “the scanner is obviously wrong.” The correct response was to verify:</p>
<ul>
<li><p>the version in <code>mix.lock</code>;</p>
</li>
<li><p>the advisory’s affected range;</p>
</li>
<li><p>the upstream fix and release notes;</p>
</li>
<li><p>the application’s exposure;</p>
</li>
<li><p>and whether the different advisory sources agreed.</p>
</li>
</ul>
<h3>The temporary exception</h3>
<p>After confirming that Decimal 3.1.1 was outside the vulnerable range, I temporarily acknowledged the advisory in <code>mix.exs</code>:</p>
<pre><code class="language-elixir">hex: [
  ignore_advisories: ["CVE-2026-32686"]
]
</code></pre>
<p>Hex 2.5.1 introduced this configuration for advisories that do not affect a project or cannot yet be acted upon. Ignored findings do not make <code>mix hex.audit</code> exit unsuccessfully. <a href="https://hexdocs.pm/hex/Mix.Tasks.Hex.Audit.html">Hex audit documentation</a>.</p>
<p>This was a targeted exception, not a declaration that security advisories were unimportant. I continued running the audit and suppressed only the advisory I had investigated.</p>
<p>Even so, the exception carried risk.</p>
<p><code>ignore_advisories</code> is scoped to an advisory identifier, not to a particular Decimal version. If a later dependency change had downgraded Decimal to a genuinely vulnerable 2.x release, the same exception could have hidden the finding. The ID can also match advisory aliases, so acknowledging the CVE may suppress the corresponding EEF and GHSA forms.</p>
<p>That is why an exception should have evidence, an owner, a removal condition, and, where practical, a constraint preventing an affected version from entering the lockfile.</p>
<h3>What else could I have done?</h3>
<p>Ignoring the advisory was not the only possible response.</p>
<p>I could have kept the branch blocked until the upstream record was corrected. I could have reported the contradictory metadata and waited for the corrected feed to reach Hex. If my project had been using Decimal below 3.0.0, the right response would have been to upgrade or add a compatible version constraint.</p>
<p>For a real vulnerability without an immediately usable update, another option would have been to remove or isolate the exposed code path, validate untrusted input before it reached Decimal, and document a formal risk acceptance.</p>
<p>What would not have helped was repeatedly updating an already-unaffected Decimal 3.1.1. The package version was not the problem; the advisory metadata was.</p>
<h3>Removing the exception was part of the fix</h3>
<p>Once the record was corrected, I removed the exception and reran the audit.</p>
<p>That removal mattered. A temporary security exception that remains indefinitely becomes a permanent blind spot. Hex even warns about ignored entries that no longer match a current finding, which is a useful prompt to clean them up.</p>
<p>My removal condition was straightforward:</p>
<ol>
<li><p>The authoritative record had to classify 3.1.1 correctly.</p>
</li>
<li><p>My lockfile still had to contain an unaffected Decimal version.</p>
</li>
<li><p><code>mix hex.audit</code> had to pass without the exception.</p>
</li>
</ol>
<p>After those conditions were satisfied, the exception no longer served a purpose.</p>
<h3>What I learned</h3>
<p>The biggest lesson was that a vulnerability finding is the start of an investigation, not the final verdict.</p>
<p>Machine-readable security data is enormously useful, but it can contain the same mistakes as any other software artifact. A single field can move through a CNA record, an OSV feed, a package registry and a CI scanner before appearing as a failed build.</p>
<p>The second lesson was that targeted dependency commands can reveal findings elsewhere in the resolved dependency graph. <code>mix deps.update mint</code> surfacing a Decimal advisory did not make it a Mint vulnerability.</p>
<p>The third lesson was that exceptions need lifecycle management. If an advisory must be acknowledged, the exception should be narrow, documented, reviewed, and removed as soon as the reason for it disappears.</p>
<p>Finally, a failed CI run is not proof that a vulnerability exists in the installed code. It is proof that a security boundary detected something requiring a decision.</p>
<p>In this case, the scanner’s conclusion was wrong. CI was still right to stop.</p>
<p>I hope you liked this blog. If you have any questions, please leave a comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Eigenvalues and Eigenvectors: A Visual Introduction]]></title><description><![CDATA[Introduction
A matrix represents a linear transformation. In other words, a matrix represents a transformation that you apply to the vectors in your coordinate space.
Say, you apply the matrix:
$$A = ]]></description><link>https://abulasar.com/understanding-eigenvalues-and-eigenvectors-a-visual-introduction</link><guid isPermaLink="true">https://abulasar.com/understanding-eigenvalues-and-eigenvectors-a-visual-introduction</guid><category><![CDATA[linear algebra ]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[Mathematics]]></category><category><![CDATA[eigenvalues]]></category><category><![CDATA[eigenvectors]]></category><dc:creator><![CDATA[AbulAsar S.]]></dc:creator><pubDate>Sun, 09 Aug 2026 11:13:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5f632ac61d914f19b97de8b2/4bf2d278-109d-4a09-bd68-e822da6f5b4a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Introduction</h2>
<p>A matrix represents a linear transformation. In other words, a matrix represents a transformation that you apply to the vectors in your coordinate space.</p>
<p>Say, you apply the matrix:</p>
<p>$$A = \begin{bmatrix} 2 &amp; 3 \\ 1 &amp; 0 \end{bmatrix}$$</p>
<p>to a vector:</p>
<p>$$\vec{v} = \begin{bmatrix} 0 \\ 2 \end{bmatrix}$$</p>
<p>like this:</p>
<p>$$\begin{bmatrix} 2 &amp; 3 \\ 1 &amp; 0 \end{bmatrix}\begin{bmatrix} 0 \\ 2 \end{bmatrix} = \begin{bmatrix} 6 \\ 0 \end{bmatrix}$$</p>
<p>You will get a new vector:</p>
<p>$$A\vec{v} = \begin{bmatrix} 6 \\ 0 \end{bmatrix}$$</p>
<p>Again, a matrix represents a linear transformation that you apply to the vectors in a coordinate space. Because of this, all the vectors in the coordinate space get transformed into new vectors. As a result, the entire coordinate grid gets transformed.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f632ac61d914f19b97de8b2/a13efd4a-3ae9-4de4-b096-89be36af2ece.png" alt="Matrix transforming a vector and the coordinate grid" style="display:block;margin:0 auto" />

<p><em>Diagram I: The matrix transforms both the vector and the coordinate grid.</em></p>
<h2>Eigenvalues and Eigenvectors</h2>
<h3>Span of a Vector</h3>
<p>Consider the vector <strong>v = (-3, 1)</strong>. All the scalar multiples of this vector lie on the same line passing through the origin. This line is called the <strong>span of the vector</strong>.</p>
<p>$$\operatorname{span}(\vec{v})=\lbrace c\vec{v}\mid c\in\mathbb{R}\rbrace$$</p>
<img src="https://cdn.hashnode.com/uploads/covers/5f632ac61d914f19b97de8b2/80445e3b-8c97-406e-a982-e4bf2ef6f469.png" alt="The span of the vector v equals negative three comma one" style="display:block;margin:0 auto" />

<p><em>The span of a nonzero vector in two-dimensional space is a line passing through the origin.</em></p>
<h3>Eigenvector</h3>
<p>When you multiply a matrix by a vector, the transformed vector may shift away from its original span; that is, its direction may change.</p>
<p>But there can be a few nonzero vectors that do not move away from their span after the transformation. Such vectors are called <strong>eigenvectors</strong>.</p>
<p>There can also be cases where the magnitude of the vector changes, but the vector remains in the same span. We can still say that it is an eigenvector.</p>
<p>For example, suppose a separate linear transformation <strong>T</strong> transforms the vector <strong>v = (2, 0)</strong> into <strong>Tv = (4, 0)</strong>. Its magnitude is doubled, but it still lies on the same line.</p>
<p><em>The vector is scaled, but it remains in the same span.</em></p>
<p>Therefore, <strong>v = (2, 0)</strong> is an eigenvector of this transformation.</p>
<h3>Eigenvalue</h3>
<p>The scalar factor by which an eigenvector is scaled is called its <strong>eigenvalue</strong>.</p>
<p>In this example, the vector is scaled by 2. Hence, its eigenvalue is <strong>2</strong>.</p>
<p>So, when a transformation represented by a matrix <strong>A</strong> is applied to an eigenvector <strong>v</strong>, the transformed vector becomes <strong>λv</strong>:</p>
<p>$$A\vec{v}=\lambda\vec{v},\qquad\vec{v}\neq\vec{0}$$</p>
<p>Here, <strong>λ</strong> is a scalar called the eigenvalue.</p>
<p>The eigenvalue is not the magnitude of the vector. It is the <strong>scaling factor</strong>. The magnitude of the vector changes by a factor of <strong>|λ|</strong>.</p>
<p>If <strong>λ &gt; 0</strong>, the vector points in the same direction. If <strong>λ &lt; 0</strong>, it points in the opposite direction. In both cases, it remains in the same span and is still an eigenvector.</p>
<p>If <strong>λ = 0</strong>, the vector is transformed into the zero vector. Zero can also be an eigenvalue.</p>
<p>In this example:</p>
<p>$$\lambda=2$$</p>
<p>Therefore, <strong>2 is the eigenvalue</strong>.</p>
<h2>Finding Eigenvalues and Eigenvectors</h2>
<p>Now that we know what eigenvalues and eigenvectors are, let us see how to calculate them for a given matrix.</p>
<p>For this, consider a square matrix:</p>
<p>$$A\in\mathbb{R}^{n\times n}$$</p>
<p><em>Note: Eigenvalues and eigenvectors are defined only for square matrices.</em></p>
<p><em>Unlike SVD, we do not calculate AᵀA. We work directly with A.</em></p>
<p>The core calculation has two main parts. First, we find the possible values of <strong>λ</strong>. After that, we use each value of <strong>λ</strong> to find its corresponding eigenvector.</p>
<h3>Form the Characteristic Equation</h3>
<p>Start by subtracting <strong>λ</strong> from every diagonal element of <strong>A</strong>:</p>
<p>$$A-\lambda I$$</p>
<p>Here, <strong>I</strong> is the identity matrix of the same size as <strong>A</strong>.</p>
<p>For example, if</p>
<p>$$A=\begin{bmatrix}a &amp; b \\ c &amp; d\end{bmatrix}$$</p>
<p>then</p>
<p>$$A-\lambda I=\begin{bmatrix}a-\lambda &amp; b \\ c &amp; d-\lambda\end{bmatrix}$$</p>
<p>Now calculate the determinant and set it equal to zero. This gives us the characteristic equation:</p>
<p>$$\det(A-\lambda I)=0$$</p>
<p>For a <strong>2 × 2 matrix</strong>, it becomes:</p>
<p>$$(a-\lambda)(d-\lambda)-bc=0$$</p>
<p>or</p>
<p>$$\lambda^2-(a+d)\lambda+(ad-bc)=0$$</p>
<h3>Solve for the Eigenvalues</h3>
<p>Now solve the characteristic equation for <strong>λ</strong>. The values of <strong>λ</strong> that satisfy this equation are the eigenvalues of <strong>A</strong>:</p>
<p>$$\lambda_1,\lambda_2,\ldots,\lambda_n$$</p>
<p>Depending on the matrix, an eigenvalue can be positive, negative, zero, repeated, or even complex.</p>
<h3>Find the Corresponding Eigenvectors</h3>
<p>For each eigenvalue <strong>λᵢ</strong>, substitute it into:</p>
<p>$$(A-\lambda_i I)\vec{v}_i=\vec{0}$$</p>
<p>Then solve the resulting system of linear equations.</p>
<p>The system will have at least one free variable. Assign convenient values to the free variables, making sure that the resulting vector is not zero, and calculate the remaining variables.</p>
<p>The resulting nonzero vector is an eigenvector:</p>
<p>$$\vec{v}_i=\begin{bmatrix}x_1 \\ x_2 \\ \vdots \\ x_n\end{bmatrix}$$</p>
<p><em>Note: The zero vector can never be an eigenvector.</em></p>
<p>Any nonzero scalar multiple of an eigenvector is also an eigenvector corresponding to the same eigenvalue.</p>
<h3>Normalize the Eigenvectors if Required</h3>
<p>Eigenvectors do not have to be unit vectors. Normalize an eigenvector only if the question asks for a unit eigenvector.</p>
<p>First, calculate its magnitude:</p>
<p>$$\left|\vec{v}_i\right|=\sqrt{x_1^2+x_2^2+\cdots+x_n^2}$$</p>
<p>Then divide the eigenvector by its magnitude:</p>
<p>$$\hat{v}_i=\frac{\vec{v}_i}{\left|\vec{v}_i\right|}$$</p>
<h3>Verify the Result</h3>
<p>Finally, check whether:</p>
<p>$$A\vec{v}_i=\lambda_i\vec{v}_i$$</p>
<p>If both sides are equal, the eigenvalue and eigenvector are correct.</p>
<h2>Short Formula Sheet</h2>
<p>$$\boxed{\det(A-\lambda I)=0}$$</p>
<p>This gives the <strong>eigenvalues</strong>.</p>
<p>For each eigenvalue, solve:</p>
<p>$$\boxed{(A-\lambda I)\vec{v}=\vec{0}}$$</p>
<p>This gives the corresponding <strong>eigenvectors</strong>.</p>
<p>Finally, verify:</p>
<p>$$\boxed{A\vec{v}=\lambda\vec{v}}$$</p>
<h2>Special Case: Repeated Eigenvalues</h2>
<p>If an eigenvalue occurs more than once, solve</p>
<p>$$(A-\lambda I)\vec{v}=\vec{0}$$</p>
<p>in the usual way and find all its linearly independent solutions.</p>
<p>A repeated eigenvalue may have fewer linearly independent eigenvectors than its algebraic multiplicity. Therefore, some matrices may not have enough independent eigenvectors to be diagonalized.</p>
<h2>Conclusion</h2>
<p>I hope this post made eigenvalues and eigenvectors a little easier to understand. If you have any questions, please leave a comment below. Thanks for reading 😊.</p>
]]></content:encoded></item><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]]></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 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 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 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>References</h3>
<ul>
<li><a href="https://topenddevs.com/podcasts/elixir-mix/episodes/combining-graphql-and-liveview-with-abul-asar-sayyad-emx-182">My Podcast</a></li>
<li><a href="https://topenddevs.com/podcasts/elixir-mix/">Elixir Mix</a> </li>
<li><a 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></channel></rss>