<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JordanTaylorJ</title>
    <description>The latest articles on DEV Community by JordanTaylorJ (@jordantaylorj).</description>
    <link>https://dev.to/jordantaylorj</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%2F833329%2F89fd05d6-6fa4-49bd-89e4-3f9a42888ff0.JPG</url>
      <title>DEV Community: JordanTaylorJ</title>
      <link>https://dev.to/jordantaylorj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jordantaylorj"/>
    <language>en</language>
    <item>
      <title>Responsive Design</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Thu, 31 Aug 2023 02:44:01 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/responsive-design-2a49</link>
      <guid>https://dev.to/jordantaylorj/responsive-design-2a49</guid>
      <description>&lt;p&gt;We all know that feeling after building a website; it's completed and deployed, and then you open it on your phone and the whole thing is distorted. &lt;/p&gt;

&lt;p&gt;I recently completed a project with &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt;, a mobile-first responsive design CSS framework that helped me to navigate mobile design using breakpoints. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;Each breakpoint, five in total, represents a common screen size from small (sm) to 2xl. Using these, you can conditionally render utility classes for each breakpoint.&lt;/p&gt;

&lt;p&gt;For example, say you have a header with some text.&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;h1&amp;gt;Welcome&amp;lt;/h1&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Tailwind utility classes we can specify some styling like font size and color.&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;h1 className='text-blue text-xl'&amp;gt;Welcome&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can add multiple utility classes for specific breakpoints.&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;h1 className='text-blue text-xl sm:text-2xl lg:text-4xl'&amp;gt;Welcome&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The breakpoint system for Tailwind CSS is mobile-first. This means that the unspecified utilities default to a mobile device (&lt;code&gt;text-xl&lt;/code&gt; in the example above). You should think of the "sm" breakpoint as the breakpoint ABOVE the smallest. The way to target a mobile device is by &lt;strong&gt;not&lt;/strong&gt; adding a prefix to your utility class. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to Apply
&lt;/h2&gt;

&lt;p&gt;When applied to text size, it's very intuitive to understand. Let's take a look at how to apply to a situation with a grid to gain a further understanding. &lt;/p&gt;

&lt;p&gt;We'll start with a basic grid.&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 className='grid gap-12 m-6'&amp;gt; ... &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that Tailwind is mobile-first. Depending on content and margins, a three column grid is likely to get distorted on a small screen. We'll start with a single column default for mobile, then specify other major breakpoints.&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 className='grid md:grid-cols-2 lg:grid-cols-3 gap-12 m-6 md:m-12'&amp;gt; ... &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice, I also adjusted the margin as screen size goes up. If you don't specify a specific breakpoint it will default to the next breakpoint below. In this example the breakpoints xs (no prefix) and sm will have a margin of 6. Breakpoints md, lg, xl, 2xl will all have a margin of 12. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional Rendering with Breakpoints
&lt;/h2&gt;

&lt;p&gt;Apart from resizing text and a grid layout, you can also choose to hide specific elements completely. In this example, I'll talk about reformatting a navigation panel from a navbar to a dropdown menu. &lt;/p&gt;

&lt;p&gt;Here I'm using the utility class &lt;code&gt;hidden&lt;/code&gt; as a default (mobile-first) for the navbar. At the medium breakpoint I'll add display as block.&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 className='hidden w-full md:block md:w-auto space-x-10'&amp;gt;
   &amp;lt;button&amp;gt;Home&amp;lt;/button&amp;gt;
   &amp;lt;button&amp;gt;Notifications&amp;lt;/button&amp;gt;
   &amp;lt;button&amp;gt;Settings&amp;lt;/button&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I'll define a menu dropdown button with the breakpoint &lt;code&gt;md:hidden&lt;/code&gt;.&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;button onClick={() =&amp;gt; setIsOpen(!isOpen)} className="block justify-right p-2 w-10 h-10 hover:text-white md:hidden"&amp;gt;Menu&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows for the navigation panel to switch at the medium breakpoint from a navbar to a dropdown menu. As one element is hidden, the other will take its place. This prevents a warped user experience on mobile but adds a fuller design/layout to a larger screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out!
&lt;/h2&gt;

&lt;p&gt;I find it best to learn through trial an error so open up a text editor and try it out. Tailwind makes it pretty easy to get the hang of and it's really exciting to watch the browser adapt. &lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>react</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Rails Live Coding</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Mon, 16 Jan 2023 21:55:59 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/rails-live-coding-57ff</link>
      <guid>https://dev.to/jordantaylorj/rails-live-coding-57ff</guid>
      <description>&lt;p&gt;My project is a website for a book club that allows users to share books and discuss them. Each review/comment has an option to add a heart or a &lt;code&gt;favorite&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The objective was to add a button to my book display page that displays the book with the highest &lt;code&gt;favorite&lt;/code&gt; count. &lt;/p&gt;

&lt;p&gt;My schema looks like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07gbw97ss0urw7jh18ur.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%2F07gbw97ss0urw7jh18ur.png" alt="Application Schema" width="800" height="699"&gt;&lt;/a&gt;&lt;br&gt;
Check out my &lt;a href="https://github.com/JordanTaylorJ/book_club" rel="noopener noreferrer"&gt;repo&lt;/a&gt; for more context. &lt;/p&gt;
&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;In the front I added a variable favoriteBook to state, a JSX button, and the onClick with the following fetch request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleFavoriteClick = () =&amp;gt; {
    fetch('/books/favorite')
    .then(res =&amp;gt; res.json())
    .then(res =&amp;gt; setFavoriteBook(res))
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, I added a custom route in routes.rb. &lt;br&gt;
&lt;code&gt;get '/books/favorite', to: 'books#favorite'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, in my Book model I added a method to determine the number of favorited reviews on a book instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def favorite_count
        count = 0
        self.reviews.each do |review| 
            if review.favorite == true
                count += 1
            end
        end 
        count
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;self&lt;/code&gt; above refers to an instance of Book in the class Book. I started the &lt;code&gt;count&lt;/code&gt; variable at zero, then iterated through each review to see if the favorite boolean was true. If it was, I added one to the &lt;code&gt;count&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In book_serializer.rb I added &lt;code&gt;:favorite_count&lt;/code&gt; to my attributes in order for it to be available to the frontend. &lt;/p&gt;

&lt;p&gt;Finally, in books_controller.rb I was able to utilize the favorite_count method. I set the &lt;code&gt;highest_count&lt;/code&gt; variable to zero and a case 'No books have been favorited' incase of a scenario where there are no favorites. I iterated through each book and determined the book with highest number of favorited reviews.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def favorite
        highest_count = 0
        favorite_book = 'No books have been favorited.'
        Book.all.each do |book|
            if book.favorite_count &amp;gt; highest_count 
                highest_count = book.favorite_count
                favorite_book = book
            end
        end
        render json: favorite_book, status: :ok 
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Other Considerations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;When I was initially fetching to the custom route I kept getting back content for books#index. I later determined that in order for my custom route to work on books_controller.rb, I needed to remove  &lt;code&gt;resources :books, only: [:show, :create]&lt;/code&gt; This &lt;code&gt;only:&lt;/code&gt; was blocking the custom route. I ended up listing out the &lt;code&gt;:show&lt;/code&gt; and &lt;code&gt;:create&lt;/code&gt; routes along with &lt;code&gt;:favorite&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  get '/books/show', to: 'books#show'
  post '/books', to: 'books#create'
  get '/books/favorite', to: 'books#favorite'

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Controller Clean-Up
&lt;/h2&gt;

&lt;p&gt;The #favorite action in books_controller.rb was originally made with only a &lt;code&gt;highest_count&lt;/code&gt; variable, and I was trying to save the book there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def favorite
        highest_count = ""
        Book.all.each do |book|
            if book.favorite_count &amp;gt; highest_count 
                highest_count = book
            end
        end
        render json: favorite_book, status: :ok 
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This failed by the second iteration because I was comparing an integer to an instance of book.&lt;/p&gt;

&lt;p&gt;The solution I made was to create two variables, one for the count and another for the book itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def favorite
        highest_count = 0
        favorite_book = 'No books have been favorited.'
        Book.all.each do |book|
            if book.favorite_count &amp;gt; highest_count 
                highest_count = book.favorite_count
                favorite_book = book
            end
        end
        render json: favorite_book, status: :ok 
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a success, but I thought could be cleaner.&lt;br&gt;
Another solution would be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def favorite
        favorite_book = Book.new(title: 'No books have been favorited.')
        Book.all.each do |book|
            if book.favorite_count &amp;gt; favorite_book.favorite_count 
                favorite_book = book
            end
        end
        render json: favorite_book, status: :ok 
    end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I'm comparing the favorite_count of the current highest book to the favorite_count of the book in the current iteration. I created an unsaved instance of Book with no reviews (and therefore no favorites) in order to make the first comparison. &lt;/p&gt;

&lt;h1&gt;
  
  
  Tips for Rails Live Coding
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Determine where in the schema the information you're looking for is. In my scenario, my &lt;code&gt;favorite&lt;/code&gt; attribute was on every individual review nested under books. I needed to calculate a count for each instance of Book before I could compare to find the highest favorite_count. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow the call stack. First I needed a button to press, then the routing, then an empty controller action. From there I had to determine the connection between all my ruby files (controllers, serializers, and models) to ensure I was utilizing them properly. Remember the models represent an instance of a table item. Serializers are for data display to frontend. Controller is sending out the response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read error messages carefully. They're helpful if you take the time to understand the feedback. Pop in a debugger or binding.pry to take a closer look at whats happening.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Rails: Resources for a Secure Database</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Wed, 30 Nov 2022 18:35:44 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/rails-resources-for-a-secure-database-fo1</link>
      <guid>https://dev.to/jordantaylorj/rails-resources-for-a-secure-database-fo1</guid>
      <description>&lt;p&gt;While building my first full stack application with rails, I learned several new skills about working with a database. This includes how to utilize gems and ruby on rails to keep the database secure. Below are my notes about the benefits of strong params, validating data, serializers, error handling, password encryption and cookies. &lt;/p&gt;

&lt;p&gt;All code examples are from my &lt;a href="https://github.com/JordanTaylorJ/book_club" rel="noopener noreferrer"&gt;BookClub&lt;/a&gt; application, built as a space to share and discuss favorite books. &lt;/p&gt;

&lt;h1&gt;
  
  
  Strong Params
&lt;/h1&gt;

&lt;p&gt;In the controllers I used strong params to define the attributes the controller expects to receive from the frontend. I created a private method of book_params to pass to methods within the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def book_params
    params.permit(:title, :author, :image)
end 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the following create action &lt;code&gt;Book.create(book_params)&lt;/code&gt;, a hash containing the attribute &lt;code&gt;:person&lt;/code&gt; would return the error: &lt;code&gt;Unpermitted parameters: :person&lt;/code&gt;. By only permitting the specified attributes, we can safely use the book_params hash for mass assignment. &lt;/p&gt;

&lt;h1&gt;
  
  
  Validations
&lt;/h1&gt;

&lt;p&gt;I utilized the built in Ruby validator in my models to protect the database from invalid data. This consists of the &lt;code&gt;validates&lt;/code&gt; keyword, an attribute, and a hash of options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;validates :username, presence: true, uniqueness: true
validates :password, presence: true, length: {in: 3..20}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These validations in the User model ensure that there are no duplicated usernames and that a password's length is between three and twenty characters. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active Record validates will only be checked when adding or updating data via Ruby/Rails. The validations will not run with SQL code via the command line. &lt;code&gt;#valid?&lt;/code&gt; is the only way to trigger validation without touching the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Serializing Associations
&lt;/h1&gt;

&lt;p&gt;The book_serializer.rb defines which attributes are included in the response data. The data for each book is nested three layers deep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "id": 96,
    "title": "the Sun and Her Flowers",
    "author": "Rupi Kaur",
    "image": "https://images.unsplash.com/photo-1545239351-cefa43af60f3?ixlib=rb-4.0.3&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=735&amp;amp;q=80",
    "reviews": [
      {
        "id": 261,
        "user_id": 83,
        "book_id": 96,
        "comment": "Impedit quia ex sunt.",
        "favorite": false,
        "user": {
          "id": 83,
          "username": "Ringo"
        }
      }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I utilize macros as well as the include keyword to get all three layers in our response data in order to display the has_many through relationship. &lt;/p&gt;

&lt;p&gt;I did not include the timestamps created_at and updated_at, as the frontend will not require this information. The has_many macro is used to attach the reviews to the associated books.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BookSerializer &amp;lt; ActiveModel::Serializer
  attributes :id, :title, :author, :image, :reviews
  has_many :reviews 
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, AMS only nests associations one level deep. In order to override this behavior, I used the &lt;code&gt;include&lt;/code&gt; keyword in books_controller.rb show method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;render json: books, include: 'reviews.user', status: :ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  React Error Handling
&lt;/h1&gt;

&lt;p&gt;To make use of the errors from Ruby, they are rendered to the user in the frontend. All components with a POST and/or PATCH request have errors kept in state&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const [errors, setErrors] = useState([]);&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;After a fetch, we check the response status. If it's okay, we can use the response as intended, otherwise, use setErrors with the error response to display the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        .then((r) =&amp;gt; {
            if (r.ok){
                r.json().then((r) =&amp;gt; setUser(r))
                setErrors([])
            } else {
                r.json().then((r) =&amp;gt; setErrors(r.errors))
            }
        })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example below, the validation errors are displayed conditionally when invalid data is used to try to create a new user account.&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%2F1zc68vh7iqyl2y040f8l.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%2F1zc68vh7iqyl2y040f8l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Password Encryption
&lt;/h1&gt;

&lt;p&gt;The macro &lt;code&gt;has_secure_password&lt;/code&gt; comes from the bcrypt gem. It's used to hash and salt all passwords for security. &lt;br&gt;
This gem requires the users table to have the column &lt;code&gt;password_digest&lt;/code&gt;. The macro is added to the User model.&lt;/p&gt;
&lt;h1&gt;
  
  
  Cookies and Sessions
&lt;/h1&gt;

&lt;p&gt;User authentication allows the application to keep track of who is or is not logged in. This is done by setting the user's username to a session hash when the user logs in. &lt;/p&gt;

&lt;p&gt;First, we install middleware in our config/application.rb file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore
config.action_dispatch.cookies_same_site_protection = :strict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the application controller, I included Cookies so that the rest of our controllers can access them. I also defined the method :authorized and set it as a before_action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ApplicationController &amp;lt; ActionController::API
  include ActionController::Cookies

  before_action :authorized 

  def authorized 
    return render json: {error: "Not Authorized"}, status: 
    :unauthorized unless session.include? :user_id
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This authorized method checks sessions to see if a user is currently logged in. The before_action will run the authorized method before every request to check that a user is logged in before proceeding. &lt;/p&gt;

&lt;p&gt;Using rails generator, I created a sessions_controller &lt;code&gt;rails g controller Session&lt;/code&gt;. Here, I define create and destroy actions for logging in and logging out. &lt;/p&gt;

&lt;p&gt;The destroy (logout) is straight forward, simply removing the user_id from session, but the create (login) action needs to be authenticated by a password. The code &lt;code&gt;user&amp;amp;.authenticate(params[:password])&lt;/code&gt; checks that a user was found and the password in params matches what is saved in the database for that user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; def create
        user = User.find_by(username: params[:username])
        if user&amp;amp;.authenticate(params[:password])
            session[:user_id] = user.id 
            render json: user, status: :created
        else 
            render json: {error: "Invalid username or 
            password"}, status: :unauthorized 
        end 
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user is found, we save the &lt;code&gt;user.id&lt;/code&gt; to the sessions hash. &lt;/p&gt;

&lt;p&gt;*** Because the authorized before action was placed in application_controller.rb, it is inherited by all the other controllers. In the sessions_controller.rb, we have to remember to skip the :authorized action for our create (login) route. We can skip :authorized any time we don't need to check that a user is logged in before proceeding with an action. &lt;br&gt;
&lt;code&gt;skip_before_action :authorized, only: :create&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I hope these notes give you some insight as to how rails can be used to keep data secure and help you in the creation of your own database. &lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Ruby, Active Record, &amp; Sinatra</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Mon, 15 Aug 2022 01:31:00 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/ruby-active-record-2d4b</link>
      <guid>https://dev.to/jordantaylorj/ruby-active-record-2d4b</guid>
      <description>&lt;p&gt;I built a web basic API with Sinatra and Active Record to support a React &lt;a href="https://github.com/JordanTaylorJ/phase-3-frontend"&gt;frontend&lt;/a&gt;. Here, I go over some key takeaways from building out the server side of this application.&lt;/p&gt;

&lt;h1&gt;
  
  
  One to Many Relationship
&lt;/h1&gt;

&lt;p&gt;The tables in this database have a one-to-many relationship. I built two tables, trails and athletes. One trail "belongs to" many athletes. Below, the entity relationship diagram shows the foreign key &lt;code&gt;trail_id&lt;/code&gt; is in the athletes table connected to the &lt;code&gt;id&lt;/code&gt; on trails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entity-Relationship Diagram:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W4fnw8AU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aojbpf7j27y6g4rdbwjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W4fnw8AU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aojbpf7j27y6g4rdbwjp.png" alt="Entity-Relationship Diagram" width="624" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Active Record
&lt;/h1&gt;

&lt;p&gt;I inherit from the Ruby gem Object Relational Mapper, Active Record in order to define the relationship between the two classes, athlete and trail in the app/models files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Athlete &amp;lt; ActiveRecord::Base
    belongs_to :trail
end
&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;class Trail &amp;lt; ActiveRecord::Base
    has_many :athletes
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use Rake commands to create my migration files:&lt;br&gt;
&lt;code&gt;bundle exec rake db:create_migration NAME=create_athletes&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;I define the structure of the database tables in the db/migrate files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateAthletes &amp;lt; ActiveRecord::Migration[6.1]
  def change
    create_table :athletes do |t|
      t.string :name
      t.string :time
      t.integer :trail_id
      t.boolean :unsupported
      t.timestamps
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to remember to follow Active Record's naming conventions by using snake_case for the migration file names and CamelCase for the class names as well as keeping the migrate files' timestamp intact. &lt;/p&gt;

&lt;p&gt;It's also important to define &lt;strong&gt;table names as plural&lt;/strong&gt; and &lt;strong&gt;class names as singular&lt;/strong&gt;. When defining the table relationships, the &lt;strong&gt;singular&lt;/strong&gt; trail &lt;code&gt;belongs_to&lt;/code&gt; and &lt;strong&gt;plural&lt;/strong&gt; athletes &lt;code&gt;has_many&lt;/code&gt; when associating the tables. &lt;/p&gt;

&lt;p&gt;Active Record expects these naming conventions in order to recognize the associations between the data; "convention over configuration". The migration file timestamps allow Active Record to manage version control/schema.&lt;/p&gt;

&lt;p&gt;In the app/controllers/application_controller file, I inherit from Sinatra(Controller) in order for the frontend (View) to connect to the database (Model). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Seo5C6Jt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techterms.com/img/lg/mvc_1321.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Seo5C6Jt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://techterms.com/img/lg/mvc_1321.png" alt="Image" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The schema.rb file shows the current version of the database. The version number corresponds to the timestamps on the migration files which is how Active Record maintains version control. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveRecord::Schema.define(version: 2022_07_20_184513) do&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I use dynamic routing to handle CRUD actions. For example, from the client side, a POST request would be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const handleAddTrail = (newTrail) =&amp;gt; {
      fetch("http://localhost:9292/trails", {
          method: 'POST',
          headers: { 
            "Content-Type": "application/json", 
          },
          body: JSON.stringify(newTrail),
        })
        .then(r =&amp;gt; r.json())
        .then((newTrail) =&amp;gt; handleAddTrailToTrails(newTrail))
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  post '/trails' do
    trail = Trail.create(
      name: params[:name],
      location: params[:location],
      distance: params[:distance],
      elevation_gain: params[:elevation_gain]
    )
    trail.to_json(include: :athletes)
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the POST request example above, it's important to remember to use &lt;code&gt;include:&lt;/code&gt; to access associated data between tables. Otherwise, in this example, the returned &lt;code&gt;newTrail&lt;/code&gt; object would not include an athletes array [].&lt;/p&gt;

&lt;h1&gt;
  
  
  Overall Thoughts on Active Record and Sinatra
&lt;/h1&gt;

&lt;p&gt;Learning the code and creating a database was not an overly complicated process with these tools. The issues I was getting hung up on were with the file structure of the backend and the specificity of naming conventions. &lt;br&gt;
I also started out with a lot of code in my application_controller file to handle requests before realizing that the associated data means I only needed an initial fetch from the trails table in order to also get all the data I needed from the athletes.&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>React BoardBuilder</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Wed, 15 Jun 2022 18:13:35 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/react-boardbuilder-efe</link>
      <guid>https://dev.to/jordantaylorj/react-boardbuilder-efe</guid>
      <description>&lt;p&gt;This idea was sparked by question based forms that sort the user into a group (Find out which Harry Potter house you belong to). I had originally planned to make a site that leads you to the perfect bike for your style of riding. I quickly realized that every question would be dependent on the previous selection and formatting the data could quickly become cluttered, so I went back to the drawing board. I still liked the idea of a question form connected to a specific outcome.&lt;br&gt;
The application I made is a similar idea, but applied to skateboards. This application works with you to build a complete skateboard based on what style of riding you want.&lt;/p&gt;
&lt;h1&gt;
  
  
  ReadMe.md
&lt;/h1&gt;
&lt;h3&gt;
  
  
  Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Homepage display with navigation panel&lt;/li&gt;
&lt;li&gt;Step-by-step guide to build out your board&lt;/li&gt;
&lt;li&gt;List created boards&lt;/li&gt;
&lt;li&gt;Delete for each board&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Tech Stack:
&lt;/h3&gt;

&lt;p&gt;JavaScript React, MUI&lt;/p&gt;
&lt;h3&gt;
  
  
  Component Hierarchy:
&lt;/h3&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%2Fxv0hwtpdkxcp1urxoo08.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%2Fxv0hwtpdkxcp1urxoo08.png" alt="Component Hierarchy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Organizing Data &amp;amp; useState
&lt;/h1&gt;

&lt;p&gt;Working with nested data proved a difficult task. I reformatted the db.json several times throughout this build to keep the code clean and minimal. I also very quickly learned the value of good event and variable names in order to better manage props. &lt;/p&gt;

&lt;p&gt;The useState hook updates React's internal state. &lt;code&gt;boards&lt;/code&gt; state was kept inside of the App component in order for the children components to have access via props. State for &lt;code&gt;newBoard&lt;/code&gt; was kept inside the BoardBuilder component along with the data for each &lt;code&gt;step&lt;/code&gt; and MUI's &lt;code&gt;activeStep&lt;/code&gt; functionality. &lt;/p&gt;
&lt;h1&gt;
  
  
  Controlled Component
&lt;/h1&gt;

&lt;p&gt;Using state to derive input value makes a controlled input. In React, rather than using a selected attribute on &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, &lt;code&gt;value={state}&lt;/code&gt; should be used for controllable props. &lt;/p&gt;

&lt;p&gt;Below you can see the complications in making &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; a controlled input. I wasn't able to pass the &lt;code&gt;part&lt;/code&gt; variable as a value attribute because it was outside of the scope.  &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%2Fyi308z89s09h1wkyb36c.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%2Fyi308z89s09h1wkyb36c.png" alt="Selector component uncontrolled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I considered reformatting my db.json again, but I wanted to avoid adding a third fetch to my application. I tried to reformat to a checkbox instead, but that became an issue when I needed to restrict the selection to a single item. Eventually, I revisited the select tag with a simpler version (without using MUI). &lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;part&lt;/code&gt; variable in state and set &lt;code&gt;value={part}&lt;/code&gt;. When a part is selected, handleChangePart saves it in state, and then it is passed to the callback function handleChange. &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%2F9dkdxxt09hqmv9y6phvy.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%2F9dkdxxt09hqmv9y6phvy.png" alt="Selector component controlled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, the handleChange inside of the BoardBuilder component is responsible for building the &lt;code&gt;newBoard&lt;/code&gt; object from the &lt;code&gt;parts&lt;/code&gt;. Note the splice to make a copy of the previous state and the object key being targeted with the &lt;code&gt;name&lt;/code&gt; value as an attribute of select.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleChange = (e) =&amp;gt; {
    setNewBoard({...newBoard, [e.target.name] : e.target.value})
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also set the initial state of a newBoard to the first available option so that it can't default to an empty string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [newBoard, setNewBoard] = useState({
    deck: 'standard',
    trucks: 'standard kingpin',
    wheels: 'skatepark',
    risers: 'risers',
    griptape: 'griptape'
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Restful API
&lt;/h1&gt;

&lt;p&gt;I use json-server for POST and DELETE requests. Like the example below, we must get the promise back from the fetch request before setting state. I used splice to make a copy, rather than modifying the original state of &lt;code&gt;boards&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const handleAddBoard = (newBoard) =&amp;gt; {
    fetch("http://localhost:3001/completeboards", {
      method: 'POST',
      headers: { 
        "Content-Type": "application/json", 
      },
      body: JSON.stringify(newBoard),
    })
    .then(r =&amp;gt; r.json())
    .then((data) =&amp;gt; setBoards([...boards, data]))
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  useEffect &amp;amp; Dependency Arrays
&lt;/h1&gt;

&lt;p&gt;I implemented the useEffect hook twice in this application. First, to fetch boards and initially &lt;code&gt;setBoards&lt;/code&gt; and again inside of the BoardBuilder component to fetch and &lt;code&gt;setStep&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I had initially thought that &lt;code&gt;boards&lt;/code&gt; should be the dependency array value so that when that value changes, the side effect fires. However, I realized this is not necessary because boards is saved in state and state is updated after each POST or DELETE to the boards data. If a an object is inside the dependency array, it will infinitely fetch. I used empty dependency arrays on both so that they each only run once when the component initially renders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    fetch("http://localhost:3001/completeboards")
    .then(r =&amp;gt; r.json())
    .then((data) =&amp;gt; setBoards(data))
  }, []);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the db.json should be run with --p 3001. &lt;/p&gt;

&lt;h1&gt;
  
  
  MUI
&lt;/h1&gt;

&lt;p&gt;I worked with &lt;a href="https://mui.com/" rel="noopener noreferrer"&gt;MUI&lt;/a&gt; for a consistent styling.  The 'Stepper' component was used (as BoardBuilder) to map through the process of building a board. Within that, I needed to also map through the &lt;code&gt;parts&lt;/code&gt; data inside of a Select component. I was able to clean up some of the BoardBuilder code from the original MUI formatting by adding &lt;code&gt;steps&lt;/code&gt; into db.json and fetching it. &lt;/p&gt;

&lt;p&gt;I also used MUI's AppBar for the navigation bar and their Card component to display the results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; The styling is consistent and clean throughout the application. It's easy to integrate and has good documentation (depending on the version, I used v5.8.4).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; Similarly to working with MaterializeCSS, I had issues adding my own styling along with MUI. There is a learning curve for adding images and changing MUI's "theme". It also clutters your code quite a bit. Theres a lot of extra imports and even some items added to state.&lt;/p&gt;

&lt;p&gt;Imports for changing theme:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import {ThemeProvider, createTheme } from '@mui/material/styles';&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  React Takeaways
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;React uses declarative code - JSX tells what to do, but not how to do it. AKA - no more document.createElement() and appending everything. 🙌&lt;/li&gt;
&lt;li&gt;React components allow for reusable code! 👏 I only needed one BoardBuilder and one Result component and was able to map through with the data I wanted.&lt;/li&gt;
&lt;li&gt;Setting state with a new object will cause a re-render with the updated info! No need to worry about complex DOM manipulation, again, less typing = efficiency. 👌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The overall key to React is understanding inverse data flow. &lt;/p&gt;

&lt;p&gt;Checkout my &lt;a href="https://github.com/JordanTaylorJ/board-builder" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo to see the full code!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>mui</category>
    </item>
    <item>
      <title>JavaScript Colorado Fourteeners</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Wed, 23 Mar 2022 05:00:23 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/phase-1-project-colorado-fourteeners-dni</link>
      <guid>https://dev.to/jordantaylorj/phase-1-project-colorado-fourteeners-dni</guid>
      <description>&lt;h1&gt;
  
  
  What Is It?
&lt;/h1&gt;

&lt;p&gt;For my Phase I project, I built a &lt;a href="https://jordantaylorj.github.io/phase-1-project-14ner/"&gt;website&lt;/a&gt; inspired by the peaks in Colorado above 14,000ft. As a native Coloradan, I have hiked a handful of Colorado's 14,000ft peaks (14ers) so I understand how hiking them all becomes a bucket list item for hikers. &lt;/p&gt;

&lt;p&gt;The website was built as a way to track how many of the peaks have been hiked or "bagged" and reference the complete list of existing 14ers in CO. Basing my website around a topic I was interested in was a key factor in keeping me motivated through the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S3I1oPbS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u0dv315371yj6mtmlsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S3I1oPbS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u0dv315371yj6mtmlsp.png" alt="Colorado Fourteener Website" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Code
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. The API
&lt;/h3&gt;

&lt;p&gt;I had access to a public API containing data on Colorado peaks. I used this data to populate the list of available peaks. I fetched the data from the API as soon as the home page was rendered so that the page wouldn't be waiting on a server request when rendering the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const APIFetch = () =&amp;gt; {
    fetch(" ")
    .then(function(response) {
        return response.json();
    })
    .then(function(data){
        console.log('data', data);
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Building Navigation
&lt;/h3&gt;

&lt;p&gt;The navigation bar was the only portion of the website built into the HTML file, because it would be displayed at all times. The other "page" elements within the nav bar were built and appended with JavaScript to avoid redirects. &lt;/p&gt;

&lt;p&gt;Each section of the nav bar had a click eventListener attached to render the new page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const homeLinkEvent = () =&amp;gt; {
    homeLink().addEventListener('click', renderHome)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Button Click Events
&lt;/h3&gt;

&lt;p&gt;The click events were originally built outside of the functions that build out each page, but this quickly became an issue. The data from the fetch and page render was outside of the scope of the button so I ended up writing those eventListeners and callback functions within each page load function.&lt;/p&gt;

&lt;p&gt;Examples in my code include the &lt;code&gt;renderGoal()&lt;/code&gt; and &lt;code&gt;renderList()&lt;/code&gt; functions in index.js.&lt;/p&gt;

&lt;h1&gt;
  
  
  Takeaways
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. Materialize CSS Pros and Cons
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://materializecss.com/about.html"&gt;Materialize CSS&lt;/a&gt; was a tool I found to quickly add a consistently formatted style to a page without having to write it all from scratch. &lt;br&gt;
I used this to add most of the style to the website, including the navigation bar, the collapsible information panel, and the button styling effect.&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;!-- Compiled and minified CSS --&amp;gt;
     &amp;lt;link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"&amp;gt;
     &amp;lt;link rel="stylesheet" href="./style.css"&amp;gt;
     &amp;lt;script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;!-- Compiled and minified JavaScript --&amp;gt;
     &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this method of styling became an issue when I needed to write some of my own formatting. My CSS formatting was overwriting Materialize formatting. I had to give my styling unique labels to differentiate between the two.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.leftJ {
    width: 25%;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's harder to tweak this type of formatting, for example, moving the button near the 14er without it opening the collapsible menu. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L-Twu0ey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufchr4gjxhoewiynonvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L-Twu0ey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufchr4gjxhoewiynonvr.png" alt="Image description" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, Materialize was perfect for what I needed, but I would recommend going all or nothing when it comes to using it to avoid overwriting the formatting.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Node Getters
&lt;/h3&gt;

&lt;p&gt;Building "node getters" was a tip I learned from the project build videos. This was a way to minimize some the repetition in my code by assigning an HTML element to a JS variable.&lt;br&gt;
For example I would frequently use this mainDiv variable to reset the main page and append new elements to it every time the nav bar was clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mainDiv = () =&amp;gt; document.getElementById('main');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Loops
&lt;/h3&gt;

&lt;p&gt;A final note on this project was a takeaway about looping through arrays. I wanted to make sure that a peak could only be added to the completed list a single time. This required me to have my button callback function loop through my array of completed items to check for a duplicate. I originally tried with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for(let peak = 0; peak &amp;lt; goalList.length; peak++){
    let mtnName = div.innerText;
    if (goalList[peak] === mtnName){
        alert("You've already added this peak to your list!");
    }
    else {
        goalList.push(div.innerText);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was never getting an alert and nothing was being appended to my completed array but I also wasn't getting errors from the console. The loop was never being run because my &lt;code&gt;goalList.length&lt;/code&gt; was never getting above zero. I tried many variations of this version before eventually getting stuck inside a loop.&lt;/p&gt;

&lt;p&gt;This issue was resolved by assigning a boolean value to a variable. The loop was able to recognize the peak name as either already in the array or not in the array and then the append happens after the for loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addToGoals = () =&amp;gt; {
    let alreadyHere = false;
    for(let peak = 0; peak &amp;lt; goalList.length; peak++) {
        let mtnName = div.innerText;
        if (goalList[peak] === mtnName){
            alreadyHere = true;
            alert("You've already added this peak to your list!");
        }
    }
    if (alreadyHere === false) {
        goalList.push(div.innerText);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There are so many resources online for learning little tricks as you go. For instance I had to look up how to enter a new line in a string. &lt;code&gt;\n&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At the beginning this project was daunting, but as I worked through each step all the material I've learned clicked into place.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>html</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Hello World!</title>
      <dc:creator>JordanTaylorJ</dc:creator>
      <pubDate>Tue, 22 Mar 2022 00:48:56 +0000</pubDate>
      <link>https://dev.to/jordantaylorj/hello-world-2an1</link>
      <guid>https://dev.to/jordantaylorj/hello-world-2an1</guid>
      <description>&lt;p&gt;I recently enrolled in an online coding program. &lt;/p&gt;

&lt;p&gt;I'll be sharing my notes, thoughts, and projects on this platform as I move through the course!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
