<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: snackboy</title>
    <description>The latest articles on Forem by snackboy (@snackcode).</description>
    <link>https://forem.com/snackcode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F167547%2Fd272f2f7-433d-492b-8ba5-cec5bb27e11a.jpg</url>
      <title>Forem: snackboy</title>
      <link>https://forem.com/snackcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/snackcode"/>
    <language>en</language>
    <item>
      <title>In the Beginning...</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Thu, 10 Oct 2024 03:40:27 +0000</pubDate>
      <link>https://forem.com/snackcode/in-the-beginning-4ood</link>
      <guid>https://forem.com/snackcode/in-the-beginning-4ood</guid>
      <description>&lt;p&gt;...Man created AI and it was good. Very good. Very very very good. By now, you've probably used ChatGPT, Gemini (aka Bard), Claude or some other variant to help with a cover letter, a resignation, or resume. &lt;/p&gt;

&lt;p&gt;You can use some AIs to generate images from a textual prompt and / or other images. Much like computing back in the 70s and 80s, we are at the forefront of a new iteration of technology - a culmination of everything that has come before. Anything that can be translated to 1s and 0s can be used by AI to discern something that hasn't existed before. &lt;/p&gt;

&lt;p&gt;In that vain, I am curious to see AI does with scripture. While this may be the beginning of a bigger project, for now I just wanted to see how Claude would imagine in SVG the first chapter of Genesis. My initial prompt was to render to an image the text of Genesis Chapter 1 which I supplied into the prompt. Claude politely told me it can't render images but it could do vector based graphics, effectively SVG. &lt;/p&gt;

&lt;p&gt;The first SVG kind of surprised me. Super simple. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zqal3smt4xel8s559l3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zqal3smt4xel8s559l3.png" alt="Version 1 - SVG rendering of the Creation story" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, super simple. Yet it is well organized and easy to interpret. If the rendering was shown to someone who had some familiarity with Biblical stories, I am fairly certain they would relate it to the story of Genesis. &lt;/p&gt;

&lt;p&gt;I inquired to Claude if it did a second rendering, would it be the same? Claude said it would not. As someone who develops applications for a living, I was somewhat surprised. In my natural state as a developer, I expect code to work the same way each time it's run. Generally speaking, software needs to have a predictable outcome for it to have a predictable use. &lt;/p&gt;

&lt;p&gt;Claude said there were a few reasons why the renders &lt;strong&gt;are&lt;/strong&gt; different &lt;br&gt;
each time (verbatim from Claude):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Non-deterministic choices: As an AI, I make many small decisions when creating these images. These decisions are somewhat random within certain parameters. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact coordinates for elements&lt;/li&gt;
&lt;li&gt;Specific sizes of shapes&lt;/li&gt;
&lt;li&gt;Color choices within a reasonable palette&lt;/li&gt;
&lt;li&gt;Which artistic elements to include or exclude&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;No visual memory: I don't actually "remember" or reference the previous image when creating a new one. Each time, I start fresh, interpreting the prompt anew.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Multiple valid interpretations: The creation story can be visualized in many different ways. Sometimes I might focus on a linear timeline, other times on a circular layout. One version might emphasize the separation of elements, another might focus on the emergence of life.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Creative problem-solving: Each time I create an SVG, I'm solving the problem of "how to represent this story visually" from scratch. This leads to different solutions each time.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Being new to AI, these are concepts I had no idea about until this little experiment. Claude has no visual memory so on it's own it can't "think" to use what it has done as input to what it is doing. Claude is randomizing certain things like colors and positioning of objects within what it calls reasonable parameters. If I had to akin this to a human behavior, I'd consider it a mood. Then there is the "creative problem-solving" and "multiple valid interpretations" - is this what we would consider "thinking?"&lt;/p&gt;

&lt;p&gt;For the moment, I am just going to call it fascinating. &lt;/p&gt;

&lt;p&gt;When I asked Claude if the rendering would be different, it gave me the response above and then proceeded to create another version to show me how it would be different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqegpjuavvd7icbs5b5nh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqegpjuavvd7icbs5b5nh.png" alt="Version 1 - SVG rendering of the Creation story" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that there are some similarities with the first SVG, but this one feels different and seems to be more understandable. That said the waters are above the land, and the lights are detached from Day 4. I think the first rendering is a better telling of the story, but the second render is more interesting to look at.&lt;/p&gt;

&lt;p&gt;As part of the second rendering, Claude added: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This variability is actually similar to how human artists work. If you asked two artists to illustrate the same story, or even the same artist to draw it twice, you'd get different results each time. The core elements would be the same, but the specific artistic choices would vary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This idea is like going to an art class where everyone is tasked with painting the same vase of flowers. Every painting will be different because each person is making decisions based on their experiences and interpretations. The main difference is that a person would likely be able to provide a reason for their decision whereas Claude has been designed to emulate decision by randomization. That is something we should be wary of when looking at AI results.  &lt;/p&gt;

&lt;p&gt;Claude offered a third version. I found this one to be a little less inspired and it's here where you can see the randomness leaking into the overall effectiveness of the outcome. Claude stated it made the following design choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout: Uses a curved dividing line with elements arranged along it, rather than the vertical or circular layouts of the previous versions&lt;/li&gt;
&lt;li&gt;Background: Incorporates a hexagonal grid pattern for a more technical/geometric feel&lt;/li&gt;
&lt;li&gt;Style: More minimalist representations of each day's creations&lt;/li&gt;
&lt;li&gt;Color Palette: Uses a different range of colors, though still keeping with the theme of creation&lt;/li&gt;
&lt;li&gt;Element Organization: Arranges the days in a flowing, cascading manner&lt;/li&gt;
&lt;li&gt;Decorative Elements: Adds horizontal lines for visual interest and structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs82vuhu680das5e51mtf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs82vuhu680das5e51mtf.png" alt="Version 1 - SVG rendering of the Creation story" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The third version has all the parts, but it feels closer to a textual list than the other two images. Reviewing the list of design choices, the SVG is not in the spirit of the Creation story. The first two kind of feel right...but perhaps that's just luck. In Claude's defense, a textual list is what the Bible or any other volume work is: lists of books that contain lists of chapters that contain lists of verses that contain lists of words. &lt;/p&gt;

&lt;p&gt;For someone who is just getting started in the AI space, I find this simple experiment very interesting. The exact same prompt was used for the three versions of Genesis 1: Chapter 1, yet Claude rendered each one differently based on - in simplistic terms - random design choices. However, the reason for those choices isn't derived internally like an artist would; but instead they are made from external ideas as if to ask what would a different artist do. The benefit here is that you can get different options, but it might take a number of tries to get the one &lt;em&gt;you&lt;/em&gt; like. I would imagine in the future to yield specific stylized results there would be distinct versions of Claude - daresay &lt;em&gt;prompt-less&lt;/em&gt; personalities?  &lt;/p&gt;

&lt;p&gt;Personally, I am looking forward to continuing my trek through the AI-verse. Just like text editors, IDEs, and compilers, AI is an extension of things we know. This difference here is that AI may yield us outcomes we don't know or even understand. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Certainly! Absolutely! I apologize!</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Fri, 27 Sep 2024 16:33:25 +0000</pubDate>
      <link>https://forem.com/snackcode/certainly-absolutely-i-apologize-2p8m</link>
      <guid>https://forem.com/snackcode/certainly-absolutely-i-apologize-2p8m</guid>
      <description>&lt;p&gt;My experience with artificial intelligence systems is rather limited. But I have been tasked with identifying projects that might be useful to our institution so when the opportunity presents itself, I try to employ AI for help.&lt;/p&gt;

&lt;p&gt;As a volunteer for our high school marching band association, I am in charge of the buttons. This includes making them with the button press as well as designing and printing. In most cases, we have PDF files on the ready so that we can just print templates as needed. But as I was checking the inventory for our home show, the "got band?" button was missing it's corresponding PDF meaning I had no digital version of the button and thus needed to start from scratch. AI to the rescue?&lt;/p&gt;

&lt;p&gt;I've heard good things about Claude.ai so I decided to start with that. My prompt and Claude's initial response was promising: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4zolfqo96owdk1kmgjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4zolfqo96owdk1kmgjf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;which yielded the following design: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu73jyx7whsb0kqkmbcor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu73jyx7whsb0kqkmbcor.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty good so far. I wanted to the button to mirror the existing design, so I followed up with some design details:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8ug9o875tj2cqdluvwi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8ug9o875tj2cqdluvwi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq3j74d4fb6w97z3crul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq3j74d4fb6w97z3crul.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was perfect! And Claude even gives you the SVG code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 225 225"&amp;gt;
  &amp;lt;!-- Button background --&amp;gt;
  &amp;lt;circle cx="112.5" cy="112.5" r="112.5" fill="black"/&amp;gt;

  &amp;lt;!-- White border --&amp;gt;
  &amp;lt;circle cx="112.5" cy="112.5" r="107.5" fill="none" stroke="white" stroke-width="5"/&amp;gt;

  &amp;lt;!-- Text --&amp;gt;
  &amp;lt;text x="112.5" y="100" font-family="Arial, sans-serif" font-size="48" font-weight="bold" text-anchor="middle" fill="white"&amp;gt;got&amp;lt;/text&amp;gt;
  &amp;lt;text x="112.5" y="150" font-family="Arial, sans-serif" font-size="48" font-weight="bold" text-anchor="middle" fill="white"&amp;gt;band?&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's compare that experience with Google's Gemini. I started with a similar prompt and Gemini yielded the following: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74mzfehlczqozjj3uho6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74mzfehlczqozjj3uho6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okaayyy...this is good information, but it didn't quite make the leap that Claude did in actually doing the design, so I asked Gemini if it could do the design:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1n84sht8d7k6bfr65sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1n84sht8d7k6bfr65sy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Answering Gemini's prompts for font, font-weight, text alignment, and border (which I said it was to be around the edge of the button), Gemini the rendered the following result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6nfjwflvdlbk3h8npak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6nfjwflvdlbk3h8npak.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not even close...but okay. After I answered "entire button" to it's question, Gemini produced this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg78i5ef9vm9mffiygpj0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg78i5ef9vm9mffiygpj0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is going on here?!?!? It looks like Gemini isn't "designing" anything, but just searching for images that "fit" the criteria. However, the persistent person I am who says "Its gotta be my fault" gave it another try.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbwufxptw4vms8z3ltov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqbwufxptw4vms8z3ltov.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7bz3lcw5sclphqjfhgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7bz3lcw5sclphqjfhgi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rwug312qp8tn6fbb44f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rwug312qp8tn6fbb44f.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Here's a revised preview:" and then no actual preview. This is just comical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuvgpczk4g6yjb2hh3f9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuvgpczk4g6yjb2hh3f9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay. I'm done. I give up. You win Gemini. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>design</category>
      <category>claude</category>
    </item>
    <item>
      <title>Blackboxing SQL in CFScript - Part 1</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Wed, 31 Jul 2024 18:40:02 +0000</pubDate>
      <link>https://forem.com/snackcode/black-boxing-sql-in-cfscript-part-1-19oo</link>
      <guid>https://forem.com/snackcode/black-boxing-sql-in-cfscript-part-1-19oo</guid>
      <description>&lt;p&gt;Working in a small development group, one of my goals is code reduction and &lt;em&gt;*&lt;em&gt;blackbox *&lt;/em&gt;&lt;/em&gt; major aspects of our system. Doing so reduces the amount of code to maintain and increases the flexibility of our systems without have to continuously rollout deployments. My first experiment was a form generator which reads a JSON "definition" file to describe a form and its data/ui components. If a form needs modified in some manner, the JSON file gets updated. If we need a new form, we just create the JSON for it.&lt;/p&gt;

&lt;p&gt;This paradigm has extended from forms to data grids, workflow, and documents. Something I've been thinking about for a while is: Can I do this for queries, and if so, should I?&lt;/p&gt;

&lt;p&gt;A development pattern that I follow when returning data to JS from ColdFusion is to run the query and then roll into JSON. CF does allow for a query to be returned directly as JSON but often times the data needs to be massaged in some way. To read more about the pattern that I use can be found &lt;a href="https://dev.to/snackcode/uncommon-practices-cfquery-to-json-2poh"&gt;here&lt;/a&gt;. In short, the pattern loops over the query results and builds an array of structures in accordance to what is needed.&lt;/p&gt;

&lt;p&gt;The process works great. But even though I am writing different queries and returning back to Angular different sets of data, the algorithmic pattern remains the same: Query -&amp;gt; Results -&amp;gt; Array -&amp;gt; Struct -&amp;gt; Return. Can generic enough code be written that would make this process easier for the developer?&lt;/p&gt;

&lt;p&gt;The first step in this endeavor is to come up with a system design that is easy to implement and maintain. My initial thoughts are that the system will break down into three aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A file that contains only the SQL. This file will sit in an area that only the Coldfusion server can see which protects it from the web. The file will contain only the SQL. Parameters in the SQL are defined as :paramName - I am not sure how I will handle those as of yet, but for query reusability, parameters need to be considered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A file to contain the definition of how the query is to map to the returned JSON. It would list all the desired fields from the query and corelated property in the JSON array of objects. For example, the database field might be &lt;code&gt;FIRST_NAME&lt;/code&gt;, but the name we pass back to the UI might be &lt;code&gt;firstName&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And finally the heart of the process: the black-box in CFScript to handle all the processing, formatting and returning of the data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SQL File
&lt;/h2&gt;

&lt;p&gt;The SQL file, where the query text will reside, is just a text file located on the server in an area that only the ColdFusion and its administrators can see. Queries, in CF, can be processed as text via &lt;code&gt;myQuery.execute( sql = 'select * from dual')&lt;/code&gt;. Parameters can be signified in the query string by using the ":" (colon) notation. (More on that implementation of this process in part 2.)&lt;/p&gt;

&lt;p&gt;Having the query in a separate text file allows for the query to be modified without modifying code. A front end query editor could be developed to allow technically oriented power users and business analysts to modify the query as needed. It also allows the easy swapping out of queries from development to production without having to move components or templates around. &lt;/p&gt;

&lt;p&gt;And while this, at some level, feels like adding a layer to the platform, the reality is that the query needs to be written and exist somewhere. The only "extra" is that it exists in a separate file from the code. &lt;/p&gt;

&lt;p&gt;A super simple query file might look like contacts.sql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select first_name,
       last_name,
       department, 
       primary_phone,
       secondary_phone
from   contacts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Definition File
&lt;/h2&gt;

&lt;p&gt;The definition is a JSON file that is used to tell the black-box what to do with a query. The definition file and the query file are a many to one. You can have multiple definition files call the same query, but a definition file can only call one query. This could be denormalized such that a definition could be used for different queries, but for our use case, this would be extra overhead to track. &lt;/p&gt;

&lt;p&gt;As such the definition file contains the query file name. For our purposes, the queries will be located in one area, but it wouldn't be a stretch to allow full paths to queries be specified. The definition also contains the data source and an array of fields and how they will map to the corresponding JSON output. &lt;/p&gt;

&lt;p&gt;Referring back to the contacts.sql above, a definition file might look like contacts.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "query" : "contacts.sql",
    "dataSource" : "myDatasource",
    "fields" : [
        { "queryField" : "spriden_first_name" , "returnProperty" : "firstName" },
        { "queryField" : "spriden_last_name" , "returnProperty" : "lastName"},
        { "queryField" : "banner_id" , "returnProperty" : "erpId"},
        { "queryField" : "home_dept" , "returnProperty" : "homeDepartment"}
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The definition file is describing to the system: find the contacts.sql and using &lt;code&gt;myDatasource&lt;/code&gt; run the query while mapping these &lt;code&gt;queryFields&lt;/code&gt; to these JSON &lt;code&gt;returnProperty&lt;/code&gt;s. &lt;/p&gt;

&lt;p&gt;But this JSON in of itself doesn't do any actual work. It's up to the black-box to do the heavy lifting. &lt;/p&gt;

&lt;h2&gt;
  
  
  Black-box CFScript
&lt;/h2&gt;

&lt;p&gt;The black-box exists in a component (.cfc) file which I called queryBox. Along with our FormGen, workflow engine, data definitions, and objects, queryBox will exist as a separate aspect of the overarching system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component {
    variables.queryPath = "[your path to queries";
    variables.queryMapPath = "[your path to query definitions / maps]";

    remote array function runQuery(_queryMap) returnformat = "json" {
        // setup array to be returned
        var _resultArray = arrayNew(1);

        //  open map file
        cffile(file = variables.queryMapPath &amp;amp; _queryMap &amp;amp; ".json" , action = "read" , variable = "_file");
        var _map = deserializeJSON(_file);

        //  from the map file, get the query
        cffile(file = variables.queryPath &amp;amp; _map.query , action = "read" , variable = "_file");
        var _sql = _file;

        // create query
        var _query = new Query();
            _query.setDatasource(_map.dataSource);

        var _result = _query.execute( sql = _sql );

        //loop through the results and build each row as a structure with using mapping
        for (_r in _result.getResult()) {
            var _struct = structNew();
            // loop through each field in the map and set the returnProperty the field value;
            for (_field in _map.fields) {
                _struct[_field.returnProperty] = _r[_field.queryField];
            }

            arrayAppend(_resultArray,_struct);
        }   
        return _resultArray;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is a JSON formatted array of objects with each object in the array a record from the query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {   firstName: 'Terry', 
        lastName: 'Beard', 
        erpId: '0000001', 
        homeDepartment: 'ITS' 
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic code can then be enhanced to do other things like data formatting and running additional processing on fields and record data. But more on that in part 2.&lt;/p&gt;

&lt;p&gt;Over the next few years, this process should significantly reduce our code base. Using this process, we can replace the queries in our existing code and have a lot less code to manage. We could also add a direct connection to the queryBox in the auth component of our system and allow the developer to skip the typical .cfcs that go with an application.&lt;/p&gt;

&lt;p&gt;This will likely be a game changer in years to come for our first party systems.&lt;/p&gt;

&lt;p&gt;Hope you found this post interesting.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>sql</category>
      <category>programming</category>
      <category>coldfusion</category>
    </item>
    <item>
      <title>Iterative Development &amp;&amp; Abstraction - Part 2</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Mon, 10 Jun 2024 03:23:35 +0000</pubDate>
      <link>https://forem.com/snackcode/iterative-development-abstraction-part-2-2dfp</link>
      <guid>https://forem.com/snackcode/iterative-development-abstraction-part-2-2dfp</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/snackcode/iterative-development-abstraction-part-1-2l9p"&gt;part 1&lt;/a&gt;, I wrote about taking blocks of static note cards and abstracting them into a carousel using a &lt;code&gt;@for&lt;/code&gt; loop. These cards were utilized on a &lt;code&gt;learn more&lt;/code&gt; page. The content for the cards were moved from the html template to the an JSON array in the controller (.ts) file. &lt;/p&gt;

&lt;p&gt;Now that this is developed and working, another iteration of abstraction can be performed to see how the carousel could be made more useful. The idea here will be to move the carousel code out of the &lt;code&gt;learn more&lt;/code&gt; template and move it into its own component. &lt;/p&gt;

&lt;p&gt;Angular makes creating and implementing components super easy, barely an inconvenience [#RyanGeorge].&lt;/p&gt;

&lt;p&gt;(Note that code snippets are at a high to mid level and are only provided to convey the idea of the development pattern.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PS D:\thecookery&amp;gt; ng generate component ui/carousel
CREATE src/app/ui/carousel/carousel.component.html (24 bytes)
CREATE src/app/ui/carousel/carousel.component.spec.ts (633 bytes)
CREATE src/app/ui/carousel/carousel.component.ts (255 bytes)
CREATE src/app/ui/carousel/carousel.component.scss (0 bytes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the component is created, the code from the &lt;code&gt;learn more&lt;/code&gt; template can be moved into carousel component. The first thing to do is replace autogenerated &lt;code&gt;carousel&lt;/code&gt; template content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p&amp;gt;carousel works!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with the &lt;code&gt;learn more&lt;/code&gt; content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for( card of cards; track card.index) {
    @if( slide == card.index) {
        &amp;lt;div class="center-element w3-margin w3-animate-opacity"&amp;gt;
            &amp;lt;div class="card" style="max-width: 800px; height:  400px;"&amp;gt;
                &amp;lt;div class="w3-padding card-title"&amp;gt;
                    {{card.title}}
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="w3-padding" style="text-align: left;height: 250px;font-size: 1.8em"&amp;gt;{{card.text}}&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div style="display: flex;justify-content: center;gap: 20px"&amp;gt;
            &amp;lt;button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-left-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-right-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, the carousel controller need to know about the slide controls. This is accomplished by moving from the learn more controller the &lt;code&gt;nextSlide()&lt;/code&gt; and &lt;code&gt;prevSlide()&lt;/code&gt; functions to the carousel and defining the necessary variables. &lt;/p&gt;

&lt;p&gt;(Note that I am doing this a bit old school Angular and not using the newer standalone component. Old school means that then the component must be registered in the &lt;code&gt;app modules&lt;/code&gt; area.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component } from '@angular/core';

@Component({
  selector: 'app-carousel',
  templateUrl: './carousel.component.html',
  styleUrl: './carousel.component.scss'
})


export class CarouselComponent {

    public slide: number = 1;
    public slideCount = 5;

    nextSlide() {
        this.slide = this.slide + 1;
        if (this.slide &amp;gt; this.slideCount) {
            this.slide = 1;
        }
    }

    prevSlide() {
        this.slide = this.slide - 1;
        if (this.slide &amp;lt; 1) {
            this.slide = this.slideCount;
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the code above, there is still a bit more work to do. The first thing is that the new carousel has no idea what the cards are as they are contained in the &lt;code&gt;learn more&lt;/code&gt; controller. Another issue to address is the slide count as we know not all carousels would have 5 slides.&lt;/p&gt;

&lt;p&gt;Addressing the first issue, the cards need to be passed into the carousel component. To do this, a &lt;code&gt;cards&lt;/code&gt; input and card object need to be created in the carousel. Address the second issue, the &lt;code&gt;slideCount&lt;/code&gt; value can be removed and replaced with the length of the cards array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, Input } from '@angular/core';

export class Card {
    title: string = '';
    text: string = '';
    index: number = 0;  
}

@Component({
  selector: 'app-carousel',
  templateUrl: './carousel.component.html',
  styleUrl: './carousel.component.scss'
})

export class CarouselComponent {

    @Input() cards: Array&amp;lt;Card&amp;gt; = [];

    public slide: number = 1;

    nextSlide() {
        this.slide = this.slide + 1;
        if (this.slide &amp;gt; this.cards.length) {
            this.slide = 1;
        }
    }

    prevSlide() {
        this.slide = this.slide - 1;
        if (this.slide &amp;lt; 1) {
            this.slide = this.cards.length;
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, any CSS specific to the carousel needs to be added its CSS file. &lt;/p&gt;

&lt;p&gt;The last step is to replace the existing learn more carousel code with new carousel component. The learn more template goes from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;style&amp;gt;

.card {
    background-image:
        linear-gradient(180deg, white 3rem, #F0A4A4 calc(3rem), #F0A4A4 calc(3rem + 2px), transparent 1px),
        repeating-linear-gradient(0deg, transparent, transparent 1.5rem, #DDD 1px, #DDD calc(1.5rem + 1px));
    box-shadow: 1px 1px 3px rgba(0,0,0,.25);
    background-color: white;
}

.card-title {
    font-size: 1.5em;
}

/*https://codepen.io/teddyzetterlund/pen/YPjEzP*/

&amp;lt;/style&amp;gt;

&amp;lt;div class="w3-center" style="padding-top: 50px;margin-bottom:20px;width: 100%"&amp;gt;
    &amp;lt;img class="w3-round-large w3-card" width=100 src="/assets/logo.png"&amp;gt;
    &amp;lt;div style="font-size: 3vw;"&amp;gt;learn more&amp;lt;/div&amp;gt;
    &amp;lt;button class="w3-button w3-round-xxlarge w3-blue w3-hover-black" [routerLink]="['/register']"&amp;gt;Become a member...&amp;lt;/button&amp;gt;          
&amp;lt;/div&amp;gt;

@for( card of cards; track card.index) {
    @if( slide == card.index) {
        &amp;lt;div class="center-element w3-margin w3-animate-opacity"&amp;gt;
            &amp;lt;div class="card" style="max-width: 800px; height:  400px;"&amp;gt;
                &amp;lt;div class="w3-padding card-title"&amp;gt;
                    {{card.title}}
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="w3-padding" style="text-align: left;height: 250px;font-size: 1.8em"&amp;gt;{{card.text}}&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div style="display: flex;justify-content: center;gap: 20px"&amp;gt;
            &amp;lt;button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-left-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-right-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="w3-center" style="padding-top: 50px;margin-bottom:20px;width: 100%"&amp;gt;
    &amp;lt;img class="w3-round-large w3-card" width=100 src="/assets/logo.png"&amp;gt;
    &amp;lt;div style="font-size: 3vw;"&amp;gt;learn more&amp;lt;/div&amp;gt;
    &amp;lt;button class="w3-button w3-round-xxlarge w3-blue w3-hover-black" [routerLink]="['/register']"&amp;gt;Become a member...&amp;lt;/button&amp;gt;          
&amp;lt;/div&amp;gt;

&amp;lt;app-carousel [cards]="cards"&amp;gt;&amp;lt;/app-carousel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;&lt;em&gt;learn more&lt;/em&gt;&lt;/strong&gt; controller can be reduced to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component } from '@angular/core';

@Component({
  selector: 'app-learn-more',
  templateUrl: './learn-more.component.html',
  styleUrls: ['./learn-more.component.scss']
})

export class LearnMoreComponent {

    public cards: Array&amp;lt;any&amp;gt; = [
        {   title:  'create and edit recipes',
            text:   `   At mealHash, you can create and edit recipes. This includes areas for ingredients, instructions, and other key points 
                        you would expect as part of recipe. 
                    `,
            index: 1
        },
        {   title:  'stores',
            text:   `   You can also add stores into your mealHash, create grocery lists and easily add and remove items from the list. 
                        Ingredients can be added to directly to your grocery  lists from your recipes allowing you manage your shopping experience.
                    `,
            index: 2
        },
        {   title:  'search',
            text:   `   Use the search page to find recipes by name or that have a particular ingredient. When you find a recipe you want to try, to can copy
                        it right to your recipe binder and then edit it to make it your own.  
                    `,
            index: 3
        },
        {   title:  'recipe feed',
            text:   `   A feed is available of your and others recipes as they are added to mealhash. Just like the search, you can add a recipe to your 
                        mealhash and modify it to make it your own.
                    `,
            index: 4
        },
        {   title: 'cost',
            text:  `    mealhash is free and we intend to keep it that way. But will gladly accept donations to keep the site running. In future, 
                        we may build out premium features as we have more mealhasher feedback. But our goal is to make mealhash a must have site
                        for your recipes, grocery lists, and meal plans.
                    `,
            index: 5
        }

    ]

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...which is effectively the content of the cards.&lt;/p&gt;

&lt;p&gt;By doing iterative development with abstraction along the way, we ended up with a carousel component that can be used elsewhere in the project. And just by modifying the one component - for example adding images to the card with an &lt;code&gt;image&lt;/code&gt; key/value pair - the capability will persist to where ever that component is implemented. &lt;/p&gt;

&lt;p&gt;Going through process of iterative development and abstraction helps you get to lower code solutions thus making your code more manageable, efficient and pliable. &lt;/p&gt;

&lt;p&gt;There is one more abstraction I will share regarding this particular component - stay tuned for part 3. If you can think of other abstractions, please feel free to share in the comments.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Iterative Development &amp;&amp; Abstraction - Part 1</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Fri, 31 May 2024 11:45:23 +0000</pubDate>
      <link>https://forem.com/snackcode/iterative-development-abstraction-part-1-2l9p</link>
      <guid>https://forem.com/snackcode/iterative-development-abstraction-part-1-2l9p</guid>
      <description>&lt;p&gt;Development is generally an iterative process going from brute dump of code on to the template to abstraction and then repeat with more abstraction until you find a balance of maintainability, efficiency, and functionality as well creating a better user experience. &lt;/p&gt;

&lt;p&gt;Sometimes, daresay most times, it's not all that obvious how a page might function or look. I wanted to build a "learn more" page of an Angular project I am reworking but didn't really know how to structure it. After a bit of experimenting, notecards seemed to be the right fit since this is a recipe site.&lt;/p&gt;

&lt;p&gt;The next thing to do was to actually create some content to go on the cards. With some basic ideas of what the site should convey to potential members and a few design iterations, the following code seemed a reasonable start:&lt;/p&gt;

&lt;p&gt;(Note: Using W3CSS for overall styling with some custom styling)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="center-element w3-margin w3-animate-opacity"&amp;gt;
    &amp;lt;div class="recipe-card" style="max-width: 800px; height:  1000px;"&amp;gt;
        &amp;lt;div class="recipe-card-title recipe-card-title-position-1"&amp;gt;
            create and edit recipes
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="recipe-card-body card" style="text-align: left;height: 250px;font-size: 2em"&amp;gt;
            At mealHash, you can create and edit recipes. This includes areas for ingredients, 
            instructions, and other key points you'd expect as part of recipe.    
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since there were 5 points of information, the above code was repeated 5 times swapping out the appropriate text for each card. This gets the information conveyed but the experience is really just a "meh". And while users generally don't care about what is happening behind the scenes, developers do.&lt;/p&gt;

&lt;p&gt;When developing, thinking (or not thinking) about the long term consequences of the code can have big impact on its maintainability. In this particular example, the question becomes: What happens if a card is to added, edited, or removed? &lt;/p&gt;

&lt;p&gt;Leaving the code as is, the answer would be open the project, CRUD the cards as necessary, test, recompile (using Angular in this instance), and redeploy. That's fine, kind of, if only an update or two is needed. But this isn't going to be a static site as over time we will be adding more features. So let's abstract a bit and while we're at it provide the potential member with a little better experience in learning more.&lt;/p&gt;

&lt;p&gt;Making the "learn more" cards function as a carousel would be a nicer user experience. Adding a &lt;code&gt;slide&lt;/code&gt; variable keeps track of which slide the user is on. &lt;code&gt;nextSlide&lt;/code&gt; and &lt;code&gt;prevSlide&lt;/code&gt; are two simple functions to move forward and backward through the slides.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nextSlide() {
    this.slide = this.slide + 1;
    if (this.slide &amp;gt; this.slideCount) {
        this.slide = 1;
    }
}

prevSlide() {
    this.slide = this.slide - 1;
    if (this.slide &amp;lt; 1) {
        this.slide = this.slideCount;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tying the functions to buttons on the card allows the user to carousel through the "learn more" content. It all comes together by wrapping a each card in a if statement that tests which slide to display.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@if( slide == 1 ) {
&amp;lt;div class="center-element w3-margin w3-animate-opacity"&amp;gt;
    &amp;lt;div class="recipe-card" style="max-width: 800px; height:  1000px;"&amp;gt;
        &amp;lt;div class="recipe-card-title recipe-card-title-position-1"&amp;gt;
            create and edit recipes
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="recipe-card-body card" style="text-align: left;height: 250px;font-size: 2em"&amp;gt;
            At mealHash, you can create and edit recipes. This includes areas for ingredients, 
            instructions, and other key points you'd expect as part of recipe.    
        &amp;lt;/div&amp;gt;
        &amp;lt;div style="display: flex;justify-content: center;"&amp;gt;
            &amp;lt;button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-left-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-right-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
}

@if( slide == 2) {
    ...
}

...

@if ( slide == 5 ) {
&amp;lt;div class="center-element w3-margin w3-animate-opacity w3-animation-left"&amp;gt;
    &amp;lt;div class="recipe-card" style="max-width: 800px;"&amp;gt;
        &amp;lt;div class="recipe-card-title recipe-card-title-position-5"&amp;gt;
            cost
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="recipe-card-body" style="text-align: left;height: 250px;font-size: 1.5em"&amp;gt;
            mealhash is free and we intend to keep it that way. But will gladly accept donations to keep the site running. In future, 
            we may build out premium features as we have more mealhasher feedback. But our goal is to make mealhash a must have site
            for your recipes, grocery lists, and meal plans.
        &amp;lt;/div&amp;gt;
        &amp;lt;div style="display: flex;justify-content: center;"&amp;gt;
            &amp;lt;button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-left-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-right-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this gets us a carousel and a better user experience. But the developer experience has now become a nightmare. Imagine if a change was needed on the cards such as styling or additional text. Each card would need to be updated which means 5 opportunities to introducing a bug into the page. &lt;/p&gt;

&lt;p&gt;The key here is that duplicate code is an opportunity for abstraction and ask: What parts of the code can be condensed and / or reused and how could the non-duplicated parts (generally data) be implemented. &lt;/p&gt;

&lt;p&gt;The solution in this instance was really simple: moved the card titles and text into JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public cards: Array&amp;lt;any&amp;gt; = [
        {   title:  'create and edit recipes',
            text:   `   At mealHash, you can create and edit recipes. This includes areas for ingredients, instructions, and other key points 
                        you would expect as part of recipe. 
                    `,
            index: 1
        },
        {   title:  'stores',
            text:   `   You can also add stores into your mealHash, create grocery lists and easily add and remove items from the list. 
                        Ingredients can be added to directly to your grocery  lists from your recipes allowing you manage your shopping experience.
                    `,
            index: 2
        },
        {   title:  'search',
            text:   `   Use the search page to find recipes by name or that have a particular ingredient. When you find a recipe you want to try, to can copy
                        it right to your recipe binder and then edit it to make it your own.  
                    `,
            index: 3
        },
        {   title:  'recipe feed',
            text:   `   A feed is available of your and others recipes as they are added to mealhash. Just like the search, you can add a recipe to your 
                        mealhash and modify it to make it your own.
                    `,
            index: 4
        },
        {   title: 'cost',
            text:  `    mealhash is free and we intend to keep it that way. But will gladly accept donations to keep the site running. In future, 
                        we may build out premium features as we have more mealhasher feedback. But our goal is to make mealhash a must have site
                        for your recipes, grocery lists, and meal plans.
                    `,
            index: 5
        }

    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Title and text are obvious as they refer the &lt;code&gt;recipe-card-title&lt;/code&gt; area of the card, and the text is in the &lt;code&gt;recipe-card-body&lt;/code&gt;. Index is used as a way to track the cards. The template can be condensed to one card. A &lt;code&gt;@for&lt;/code&gt; is used to iterate over the JSON array based on one of the card blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for( card of cards; track card.index) {
    @if( slide == card.index) {
        &amp;lt;div class="center-element w3-margin w3-animate-opacity"&amp;gt;
            &amp;lt;div class="card" style="max-width: 800px; height:  400px;"&amp;gt;
                &amp;lt;div class="w3-padding card-title"&amp;gt;
                    {{card.title}}
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="w3-padding" style="text-align: left;height: 250px;font-size: 1.8em"&amp;gt;{{card.text}}&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div style="display: flex;justify-content: center;gap: 20px"&amp;gt;
            &amp;lt;button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-left-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"&amp;gt;&amp;lt;span class="bx bx-right-arrow" style="font-size: 2em;" &amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a card needs to be changed, it only has to be done one time. All the data elements relevant to the card are pulled from the JSON array which makes updating the "learn more" section much easier. Updating the "learn more" page just means updating the array. This still means going through opening the project, making the mods, testing and deploying...but with a bit more abstraction, that process can be made much smoother. Stay tuned for part 2.&lt;/p&gt;

&lt;p&gt;If you got this far, thanks for reading. I don't post that often so please be nice - but I do welcome construction feedback. &lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
      <category>angular</category>
    </item>
    <item>
      <title>CFQUERY to JSON</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Thu, 09 Feb 2023 00:04:28 +0000</pubDate>
      <link>https://forem.com/snackcode/uncommon-practices-cfquery-to-json-2poh</link>
      <guid>https://forem.com/snackcode/uncommon-practices-cfquery-to-json-2poh</guid>
      <description>&lt;h2&gt;
  
  
  Rolling Your Own: CFQUERY to JSON
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Definitions:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;CFML&lt;/strong&gt; - Adobe ColdFusion Mark-Up Language&lt;br&gt; &lt;strong&gt;JSON&lt;/strong&gt; - JavaScript Object Notation&lt;br&gt;&lt;strong&gt;dual&lt;/strong&gt; (in query) - Oracle's generic way of returning data without an actual table&lt;br&gt;&lt;strong&gt;cfdump&lt;/strong&gt; - outputting the contents of a variable in CF&lt;br&gt;&lt;strong&gt;cfScript&lt;/strong&gt; - ColdFusion's version of scripting for CFML&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I first started diving into JavaScript and getting data from the Coldfusion server, I was completely confused. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;serialize, deserialize, structs, arrays, JSON, objects....ahhhhhhh&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Generally, I approach new programming areas with very little finesse and a lot of brute force. So my initial attempt to transform the cfQuery results to JSON by using &lt;code&gt;serializeJSON&lt;/code&gt; in CFML. This function takes your CF variable, whether it be an string, array, structure, etc., and converts it to something JavaScript can understand. Going the other way is deserializeJSON, which takes your JSON and converts it to something CF can understand. It's a pretty nifty system but not without it's inconveniences.&lt;/p&gt;

&lt;p&gt;Consider the following very simple query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select  'Terry' as first_name,
        'Beard' as last_name,
        'Low Key Blogger' as title
from    dual
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we cfdump the query results, we get something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
       &lt;td&gt;RESULTSET&lt;/td&gt;
       &lt;td&gt;
&lt;table&gt;
               &lt;tr&gt;&lt;td&gt;query&lt;/td&gt;&lt;/tr&gt;
           &lt;tr&gt;
&lt;td&gt; &lt;/td&gt;
           &lt;td&gt;FIRST_NAME&lt;/td&gt;
           &lt;td&gt;LAST_NAME&lt;/td&gt;
           &lt;td&gt;TITLE&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
                     &lt;td&gt;Terry&lt;/td&gt;
             &lt;td&gt;Beard&lt;/td&gt;
             &lt;td&gt;Low Key Blogger&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/table&gt;
    &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;CACHED&lt;/td&gt;
         &lt;td&gt; false &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
&lt;td&gt;EXECUTIONTIME&lt;/td&gt;
             &lt;td&gt;0 &lt;/td&gt;
        &lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;SQL&lt;/td&gt;
             &lt;td&gt;select  'Terry' as first_name,'Beard' as 
                 last_name, 'Low Key Blogger' as title from dual 
             &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Suppose we have a function that was called from our JavaScript (or other language) application and we need to process the information from the query. What we can do is take the cfquery results and serialize them but doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serializeJSON(myQuery)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"COLUMNS":["FIRST_NAME","LAST_NAME","TITLE"],"DATA":[["Terry","Beard","Low Key Blogger"]]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yikes! This formatting is not very practical when trying to display data. When first working with this and not knowing any better, I wrote a little JavaScript function that transformed the JSON results to something more useable. In a later version of CF, a parameter was added where you could get the query data in a more amendable format using the keyword struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serializeJSON(myQuery, 'struct')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{"FIRST_NAME":"Terry","LAST_NAME":"Beard","TITLE":"Low Key Blogger"}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we are getting closer to a much more useable format with an array of objects. In fact, you could use this format if you didn't need anything else. But I wanted a bit more control of the column names and field formatting so that the HTML template looked cleaner. Rather than seeing something in the template that says &lt;code&gt;{{myData.FIRST_NAME}}&lt;/code&gt;, I wanted the code to be more visually friendly and inline with what you would normally see in JavaScript...some thing like &lt;code&gt;{{myData.firstName}}&lt;/code&gt;. In the instance of the example query, we could change the field name to &lt;code&gt;firstName&lt;/code&gt;, but CF is quite happy with converting all your column names to CAPS. &lt;/p&gt;

&lt;h3&gt;
  
  
  [CF] Function Declaration
&lt;/h3&gt;

&lt;p&gt;What I've done is gotten into the habit of rolling my own struct or array (which ever is necessary) from query data in my CF functions. While this seems a little unnecessary for just formatting sake, often times I like to do extra processing on the query results. This maybe formatting dates, or getting additional data based on some attribute in the query. &lt;/p&gt;

&lt;p&gt;The first thing to note is that rather than doing a &lt;code&gt;serializeJSON&lt;/code&gt; on the &lt;code&gt;return&lt;/code&gt; step of your function, you can specify the format of your data return in the function declaration that's getting the data. For example, suppose I have a function called getEmployees designed to return employee data back to my JavaScript page. The function declaration (in cfScript) would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remote array function getEmployees() returnformat = 'JSON' {
    ... do some stuff ...
    return myData;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Forming the Array
&lt;/h3&gt;

&lt;p&gt;Most of the time I am returning arrays of data. The basic idea is to execute a query, loop through the query's result, create a struct to hold a row of data, and then append that struct to the array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remote array function getEmployees() returnformat = 'JSON' {
    var _sql = 'select * from employees order by last_name, first_name';
    var _qEmployees = new Query();

    _qEmployees.setDatasource(myDS);

    var _rEmployees = _qEmployees.execute( sql = _sql );

    var _employees = ArrayNew(1)

    for (_e in _rEmployees.getResult()){
        var _employee = structNew();

        _employee['firstName'] = _e.first_name;
        _employee['lastName'] = _e.last_name;
        _employee['title'] = _e.title;
        _employee['hireDate'] = dateformat(_e.hire_date,'short');

        arrayAppend(_employees,_employee);
    }

    return _employees;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two lines in the above example function is to define the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var _sql = 'select * from employees order by last_name, first_name';
var _qEmployees = new Query();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we set the query's datasource, i.e. where the data is coming from and then &lt;code&gt;.execute&lt;/code&gt; the query and put the results of the query into the variable &lt;code&gt;_rEmployees&lt;/code&gt;. An array called &lt;code&gt;_employees&lt;/code&gt; is created to hold our processed data. We loop through the data using a &lt;code&gt;for&lt;/code&gt; loop where &lt;code&gt;_e&lt;/code&gt; holds each row as we go through the &lt;code&gt;.getResult&lt;/code&gt;, effectively the result set.  &lt;/p&gt;

&lt;p&gt;Inside the loop, we define the &lt;code&gt;_employee&lt;/code&gt; structure to hold the row of data. Then we assign each column of data (that we want) to the corresponding property of the &lt;code&gt;_employee&lt;/code&gt; structure. It's important to note the syntax of the &lt;code&gt;_employee[]&lt;/code&gt; specification. CF allows you to use dot notation to make these assignments as well. For example you can do &lt;code&gt;employee.firstName = _e.first_name&lt;/code&gt;. However, by default, CF will return the property as all CAPS. When using bracket notation for the structure, the CF returns the property name as is. &lt;/p&gt;

&lt;p&gt;The next step in the loop is to add the &lt;code&gt;_employee&lt;/code&gt; to the &lt;code&gt;_employees&lt;/code&gt; collection which is done with &lt;code&gt;arrayAppend&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;After the loop has run it's course, the final step is to &lt;code&gt;return&lt;/code&gt; the data back to the calling process.&lt;/p&gt;

&lt;p&gt;The result is very similar to just returning the query using the &lt;code&gt;serializeJSON(_qEmployees,'struct')&lt;/code&gt; but with lower case formatting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{"lastName":"Stark","hireDate":"12/1/18","firstName":"Tony","title":"Iron Man"},
{"lastName":"Parker","hireDate":"12/1/18","firstName":"Peter","title":"Spider Man"},
{"lastName":"Odinson","hireDate":"12/1/18","firstName":"Loki","title":"Prince of Asgard"}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;While it's not necessary to roll your own JSON, there are some benefits in doing so. First is readability on the JavaScript and template side of the application. Having the both the middle and presentation tiers match on their data elements goes a long way toward maintenance and debugging. The second benefit is being able to easily do some post processing on the query's result set. A small example in the above process is where I formatted the hire date. But sometimes you need to pull data from somewhere else that, while it might be possible inside the query, it may not be wise. The biggest benefit is that it is a pattern which can be easily followed and understood a year or two or five later.&lt;/p&gt;

&lt;p&gt;Hope you found this helpful. Feel free to leave a comment below and let me know what you think. &lt;/p&gt;

</description>
      <category>json</category>
      <category>coldfusion</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Menu [Re]Factoring</title>
      <dc:creator>snackboy</dc:creator>
      <pubDate>Wed, 04 Jan 2023 04:46:17 +0000</pubDate>
      <link>https://forem.com/snackcode/menu-refactoring-2jlh</link>
      <guid>https://forem.com/snackcode/menu-refactoring-2jlh</guid>
      <description>&lt;p&gt;While working on a new[er] side project, I was adding menu items to a sidebar navigation and realized the code could probably be simplified by using a bit of JSON. This particular project is in Angular, but the pattern is not Angular specific. &lt;/p&gt;

&lt;p&gt;The menu in menu.component.html looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;nav&amp;gt;
    &amp;lt;div class="nav-title"&amp;gt;Recipes&amp;lt;/div&amp;gt;
    &amp;lt;ul&amp;gt;&amp;lt;li class="nav-item" [routerLink]="['/recipes']"&amp;gt;
            &amp;lt;i class="bx bxs-food-menu"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;My Recipes&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item" [routerLink]="['/edit-recipe/new']"&amp;gt;
            &amp;lt;i class="bx bxs-plus-circle"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;New Recipe&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item" [routerLink]="['/grocerylists']"&amp;gt;
            &amp;lt;i class="bx bxs-shopping-bag"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Grocery Lists&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;i class="bx bxs-search"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Search&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;i class="bx bxs-help-circle"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Help&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
    &amp;lt;div class="nav-title"&amp;gt;Account Management&amp;lt;/div&amp;gt;
    &amp;lt;ul&amp;gt;&amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;i class="bx bxs-user"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Profile&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;i class="bx bx-home-heart"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Support Us&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;i class="bx bx-support"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Contact Us&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
        &amp;lt;li class="nav-item" (click)="logout()"&amp;gt;
            &amp;lt;i class="bx bx-log-out-circle"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Logout&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt; 
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally, I try not to abstract unnecessarily. But in this case, given the simplicity of the routing and icons, and not knowing what the future might hold, putting the menu into JSON and iterating through it might be the way to go. &lt;/p&gt;

&lt;p&gt;Reviewing the current menu, there are two sections: Recipes and Account Management. So in menu.component.ts, the following array has been added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public menu: Array&amp;lt;any&amp;gt; = [
        { section: 'Recipes' },
        { section: 'Account Managment'}
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding html template now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;nav&amp;gt;
        &amp;lt;div *ngFor="let section of menu"&amp;gt;
            &amp;lt;div class="nav-title"&amp;gt;{{section.section}}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, menu items need to be added to each section. For this particular application, each menu item will be required to have a route, name, and icon (&lt;a href="https://boxicons.com/" rel="noopener noreferrer"&gt;boxicons&lt;/a&gt;). In making sure this theory is going to work out, the menu has been updated with the first item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public menu: Array&amp;lt;any&amp;gt; = [
        {   section: 'Recipes', 
            items: [
                {   name: 'My Recipes', 
                    route: '/recipes',
                    icon: 'bxs-food-menu'
                }
            ]    
        },
        { section: 'Account Managment'}
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after a bit of testing and working out the code, here is the updated template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;nav&amp;gt;
    &amp;lt;div *ngFor="let section of menu"&amp;gt;
        &amp;lt;div class="nav-title"&amp;gt;{{section.section}}&amp;lt;/div&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;li class="nav-item" [routerLink]="[item.route]"  *ngFor="let item of section.items"&amp;gt;
                &amp;lt;i class="bx {{item.icon}}"&amp;gt;&amp;lt;/i&amp;gt;
                &amp;lt;span&amp;gt;{{item.name}}&amp;lt;/span&amp;gt;
            &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class="nav-title"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;ul&amp;gt;&amp;lt;li class="nav-item" (click)="logout()"&amp;gt;
            &amp;lt;i class="bx bx-log-out-circle"&amp;gt;&amp;lt;/i&amp;gt;
            &amp;lt;span&amp;gt;Logout&amp;lt;/span&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
 &amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angular loops over the menu to get each section, and then loops over the section to get the menu items. This is pretty much the final template code including the logout function. Technically, it could be made a route and mesh into the JSON menu, but for now it's a function within the menu component. &lt;/p&gt;

&lt;p&gt;The controlling menu array is much easier to manage. New sections and menu items can be easily added or removed. This paradigm could be extended to include roles for portal type menus. You could also put the menu in a separate JSON file and modify outside the application thus yielding the ability to update the menu without redeploying the application. The caveat to that idea is in maintaining the routes because they are not generally programmatically loaded (although that could just be my inexperience showing).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public menu: Array&amp;lt;any&amp;gt; = [
    {   section: 'Recipes', 
        items: [
            {   name: 'My Recipes', 
                route: '/recipes',
                icon: 'bxs-food-menu'
            },
            {   name: 'New Recipe',
                route: '/edit-recipe/new',
                icon: 'bxs-plus-circle'
            },
            {   name: 'Grocery Lists',
                route: '/grocerylists',
                icon: 'bxs-shopping-bag'
            },
            {   name: 'Search',
                route: '/search',
                icon: 'bxs-search'
            },
            {   name: 'Help',
                route: '/help',
                icon: 'bxs-help-circle'
            }
        ]    
    },
    {   section: 'Account Managment',
        items: [
            {   name: 'Profile',
                route: '/profile',
                icon: 'bxs-user'
            }, 
            {   name: 'Support Us',
                route: '/support',
                icon: 'bx-home-heart'
            }, 
            {   name: 'Contact Us',
                route: '/contact',
                icon: 'bx-support'
            }
        ]
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final result: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0vnecjizmsuucl23aqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0vnecjizmsuucl23aqi.png" alt="Image of each item in the menu" width="326" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you found this interesting and easy to follow. I will never profess to be a best practice kind of developer. My goal is to get things working as quickly as possible with an eye toward enhancements, maintenance, and readability. Good or otherwise, I'd love to hear your feedback as that helps me become a better developer.&lt;/p&gt;

&lt;p&gt;Cheers! &lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>seo</category>
      <category>webdev</category>
      <category>markdown</category>
    </item>
  </channel>
</rss>
