Blog

Rewriting A Static Website Using Gatsby and GraphQL – Part 2

This is part 2 of a 3 part series about the decision to rewrite my personal site using GatsbyJS and GraphQL. In my last post, I discussed how to query against static Yaml files in order to display my data; things like speaking engagements, blog posts, etc. In the process I went over concepts like GraphQL queries, GatsbyJS plugins and JSX considerations (and errors, lots of errors). Go read that post first if you want to follow along in order.

Removing Liquid Templating

Now that I have the data from my Jekyll site copied over and playing nicely with Gatsby, I have to address some of the incompatible markup I was previously using in my html. In my Jekyll site, I was using Liquid to embed some simple logic into my pages; things like if/else statements, loops, etc.

Loops

So to start, I’m going to replace all the loops I was using, like this one.

{% for speaking in site.data.speaking reversed %}
        <div class="conference">
             <p class="date">{{ speaking.date }}</p>
        </div>
{% endfor %}

I can do this by replacing the loops with JSX map functions, so it looks like this.

                        
{data.allSpeakingYaml.edges.map(({ node }) => (
       <div key={node.date} className="conference">
            <p className="date">{node.date}</p>
       </div>
))}

Note that the previous loop went through the list in reverse. I’ll worry about that piece later, this works for now.

If Statements

So now that I’ve replaced all my loops I’m going to turn my attention to the if statements. Most of the if/else statements I’m using are for styles and display. Unfortunately, since Liquid does not work in JSX, I need alternative ways of triggering these cases.

So in this first case I’m assigning a style depending on whether or not an element is the first element in a list.

<li class="wrapper 
{% if forloop.first %} style2 {% else %} style1 {% endif %}
">

Lucky for me this is relatively straightforward. That’s because I can determine whether or not the element is the first item by its index. So I can embed a ternary operator in the class assignment and check if the index is zero or not.

<li className = {index === 0 ? 'wrapper style2' : 'wrapper style1'}>

Wonderful. Ternary operators are incredibly helpful. My next case is slightly more complicated because I’m not controlling the class but the existence of the element itself. Not only that, but I’m not checking for the existence of the element but the existence of an attribute of the element.

{% if talk.video %}
    ( <a target="_blank" href="{{ talk.video }}">Video</a> ) 
{% endif %}

One of the benefits of the code we wrote in that last blog is that the second consideration is no longer relevant. In the previous post we determined that we now reference title and video as top level objects inside the loop, instead of talk.title or talk.video. Yet again, the answer is a ternary operator.

<a className={video ? "" : "emptyvideo"} target="_blank" href={video}>
      Video
</a>

The above code determines the existence of video and assigns a class to the div. All that’s left is to disappear the content inside the div with that class, like this.

.emptyvideo {
     display: none;
}

Directional Loops

The final Liquid reference I need to remove is actually related to something we’ve already seen. Remember the `reversed` notation I was using in my loops above?

{% for speaking in site.data.speaking reversed %}
     <div class="conference">
         <p class="date">{{ speaking.date }}</p>
     </div>
{% endfor %}

Now it’s time to handle that functionality. Previously, I was looping through my Yaml file in the order that I’d listed the data within that file. That meant I could add new talks or blog posts to the bottom of my Yaml file and it would display that content first. I still want to do that so I don’t accidentally mess up the file by adding new stuff to the top of it.

Unfortunately, I can’t reverse the map function we’re now using in JSX. I need to figure out another way. As it turns out, going back to my GraphQL query is the answer. However, you can’t sort in GraphQL against the implicit ordering of your resource, in this case the order that I listed my data in the Yaml file. So, I need something within that data to sort on. Lucky for me my speaking data has dates included in the attributes.

So using “reverse” chronology just means updating my query to pull in the data using those dates, with the most recent one first.

  {
    allSpeakingYaml (sort: {fields: [date], order: DESC}) {
        edges {
            node {
                conference
                year
                url
                date
                location
                image
                talks {
                    title 
                    video
                }
            }
        }
    }
}

Now my map is displaying the data in reverse chronological order because the data is given to it in that order. Even though I had a date field to use in this case that isn’t strictly necessary. I could have just as easily added an id or index or whatever else would give me ordering. And note that it isn’t necessary for the field GraphQL is using to sort to be one of the attributes queried for return.

After all of this I finally have fully functionally pages written with JSX and accessing my Yaml data. However, I’m not currently taking advantage of one of the best parts of Gatsby, image processing. I’ll tackle that in the next post.

Categories: Blog, Software Development

Tags: ,

Laurie Barth
19 Feb, 2019