<?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: Peter Phillips</title>
    <description>The latest articles on DEV Community by Peter Phillips (@phillipspc).</description>
    <link>https://dev.to/phillipspc</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%2F175926%2Fa352c417-834b-4bb0-8c47-0df14ac278e1.jpeg</url>
      <title>DEV Community: Peter Phillips</title>
      <link>https://dev.to/phillipspc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/phillipspc"/>
    <language>en</language>
    <item>
      <title>Toggling page content with Turbo Frames and Kredis</title>
      <dc:creator>Peter Phillips</dc:creator>
      <pubDate>Fri, 14 Jan 2022 14:51:03 +0000</pubDate>
      <link>https://dev.to/phillipspc/toggling-page-content-with-turbo-frames-and-kredis-20no</link>
      <guid>https://dev.to/phillipspc/toggling-page-content-with-turbo-frames-and-kredis-20no</guid>
      <description>&lt;p&gt;Wanting to show/hide content on a page is an incredibly common task, and we're probably all familiar with the &lt;a href="https://www.w3schools.com/howto/howto_js_accordion.asp" rel="noopener noreferrer"&gt;traditional ways of doing this using JavaScript&lt;/a&gt;. While there's nothing &lt;em&gt;wrong&lt;/em&gt; with this, it may not be the best approach in all cases. For example, one major drawback is that &lt;strong&gt;if you reload the page, it will be back in its default state&lt;/strong&gt;. Something you've hidden will be shown again, or vice versa. &lt;/p&gt;

&lt;h3&gt;
  
  
  So what if we want something we hide to stay hidden the next time we come back to a page? 🤔
&lt;/h3&gt;

&lt;p&gt;I'm glad you asked! That's exactly what I'm going to show you how to do in Rails, using &lt;a href="https://turbo.hotwired.dev/handbook/frames" rel="noopener noreferrer"&gt;Turbo Frames&lt;/a&gt; and the newly released gem &lt;a href="https://github.com/rails/kredis" rel="noopener noreferrer"&gt;Kredis&lt;/a&gt;. We'll be toggling content in a way that persists across page refreshes/navigation, is super quick to implement, and best of all, requires 0 lines of JavaScript 🙌.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prerequesites: You'll need a Rails application, version 6 or higher, with Hotwire/Turbo installed and a Redis connection. It should also have authentication in place (Devise, etc.) and a User model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The main elements of this solution are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Kredis to add a Redis-backed attribute on our User model to store the state&lt;/li&gt;
&lt;li&gt;Wrap the content in a &lt;code&gt;turbo_frame_tag&lt;/code&gt;, including a link that will function as a hide/show button&lt;/li&gt;
&lt;li&gt;Update the Kredis attribute in our controller and re-render the turbo frame&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it!&lt;br&gt;
So for our example, let's say we have a chart on our Dashboard. Some users may want to see it every time they log in, others might prefer to have it hidden.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: add a Kredis attribute to our User model
&lt;/h2&gt;

&lt;p&gt;Once you've gone through the simple steps to &lt;a href="https://github.com/rails/kredis#installation" rel="noopener noreferrer"&gt;install the Kredis gem&lt;/a&gt;, we're ready to add the Kredis attribute to our User model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;kredis_boolean&lt;/span&gt; &lt;span class="ss"&gt;:hide_dashboard_chart&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hide_dashboard_chart?&lt;/span&gt;
    &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;hide_dashboard_chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've opted for &lt;code&gt;hide&lt;/code&gt; here because we want the default behavior to be showing the chart, and hiding it to be an opt-in action. I've also added a helper method that makes accessing the value slightly easier and also accounts for the initial value being &lt;code&gt;nil&lt;/code&gt; (which we'll treat as false).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Wrap the content we want to show/hide in a &lt;code&gt;turbo_frame_tag&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/pages/dashboard.html.erb

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"dashboard_chart"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-between"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-bold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Q1 Growth
    &lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide_dashboard_chart?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"Show"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toggle_dashboard_chart_path&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide_dashboard_chart?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://quickchart.io/chart?width=400&amp;amp;height=240&amp;amp;chart={type:'bar',data:{labels:['January','February', 'March','April', 'May'], datasets:[{label:'Dogs',data:[50,60,70,180,190]},{label:'Cats',data:[100,200,300,400,500]}]}}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;So what's going on here?&lt;/em&gt;&lt;br&gt;
We have a &lt;code&gt;turbo_frame_tag&lt;/code&gt; which we've given a name of "dashboard_chart". Inside this frame, there is a top portion with a header and a link. The link shows the words "Show" or "Hide" depending on the value of &lt;code&gt;hide_dashboard_chart&lt;/code&gt; on the &lt;code&gt;current_user&lt;/code&gt;. This link points to our &lt;code&gt;toggle_dashboard_chart_path&lt;/code&gt;, which we'll take a closer look at in a moment.&lt;/p&gt;

&lt;p&gt;Below that is the chart itself, for which I'm using a very simple link-based &lt;a href="https://quickchart.io" rel="noopener noreferrer"&gt;quickchart&lt;/a&gt;. If our &lt;code&gt;hide_dashboard_chart&lt;/code&gt; kredis attribute is false, we don't show the chart at all.&lt;/p&gt;

&lt;p&gt;We're also using Tailwindcss for some &lt;em&gt;very&lt;/em&gt; light styling. It'll look something like this:&lt;br&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%2F8p3nh4y32ipnreojghv5.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%2F8p3nh4y32ipnreojghv5.png" alt="screenshot of the page with a chart"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Update the Kredis attribute in our controller and re-render the turbo frame
&lt;/h2&gt;

&lt;p&gt;We'll need to add a route for the link in our turbo frame&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/routes.rb&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"toggle_dashboard_chart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s2"&gt;"pages#toggle_dashboard_chart"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Also, let's go ahead and put the view content from Step 2 in a partial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;# app/views/pages/dashboard.html.erb

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"dashboard_chart"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Let's write a helper method on the User model&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;toggle_hide_dashboard_chart!&lt;/span&gt;
    &lt;span class="n"&gt;hide_dashboard_chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;hide_dashboard_chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then put it all together the controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/pages_controller.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;toggle_dashboard_chart&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle_hide_dashboard_chart!&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;partial: &lt;/span&gt;&lt;span class="s2"&gt;"dashboard_chart"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Believe it or not, we're done!&lt;br&gt;
Now when we click the link in the Turbo frame, we'll make a round trip to the server where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we toggle the &lt;code&gt;hide_dashboard_chart&lt;/code&gt; Kredis attribute&lt;/li&gt;
&lt;li&gt;we return that exact same &lt;code&gt;dashboard_chart&lt;/code&gt; partial&lt;/li&gt;
&lt;li&gt;Only the Turbo frame on our dashboard page updates, this time with the new state, showing/hiding the chart accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's see it in action! 🎥
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/TDGgDqtwaUqEw9qcp7/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/TDGgDqtwaUqEw9qcp7/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;I've really been enjoying using Hotwire/Turbo, and it presents the opportunity to rethink a lot of ways we handle some common interactions on the web. Hiding/showing some view content &lt;em&gt;by making a round trip to the server&lt;/em&gt; might feel strange at first, but I think it actually presents some advantages, notably: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the code is super simple&lt;/li&gt;
&lt;li&gt;we can easily store/reuse the user's preference after a page refresh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And while we &lt;em&gt;could&lt;/em&gt; store these types of preferences in our main database, Redis feels much more appropriate to me. If we somehow lose it, no big deal! And we have total flexibility to change things around later (if our views change) without doing a database migration. Kredis giving us a nicer API to interact with Redis is just icing on the cake.&lt;/p&gt;

&lt;p&gt;There are a couple downsides worth mentioning. While the round trip to the server will be imperceptible to most users, those with very slow connections might notice a delay. We also aren't doing any sort of animations to smooth things out here. The chart pops in and out, instantly moving the rest of the page up or down, and some might find that a bit jarring. But overall, I think the pros easily outweigh the cons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking things further
&lt;/h2&gt;

&lt;p&gt;This is fine as a simple proof of concept, but I think for use in production, it could be easily improved in a few obvious ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace the "Hide"/"Show" link text with some appropriate icons&lt;/li&gt;
&lt;li&gt;We could certainly be a lot more RESTful, perhaps by making a &lt;code&gt;POST&lt;/code&gt; request to something like &lt;code&gt;users#update&lt;/code&gt; (since after all we are updating a value on the User model)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks so much for reading!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>hotwire</category>
      <category>redis</category>
    </item>
  </channel>
</rss>
