Lazy Loading a Turbo Frame

Let’s stretch this a little. Let’s add one more feature: a count of the number of favorited items in the header. You can think of this as being broadly analogous to a shopping cart. And along the way, I’ll show you one more Turbo feature: lazy loading.

Lazy loading allows us to separate the static and dynamic parts of a page so that the static part can be cached and the dynamic part automatically fills itself in when the page is loaded. If the dynamic part is slow, then this technique can also make the rest of the page appear faster and give the user a loading sign or something while the slow part of the page catches up.

To make this work, we want an entry in the navigation bar that is a placeholder for the favorites count:

 <div class=​"flex justify-items-end text-gray-300
  px-3 py-2 text-sm font-bold"​>
  <span>Favorite Count:</span>
 <%=​ turbo_frame_tag(
 "favorites-count-number"​,
 src: ​favorites_path(​count_only: ​​true​)
  ) ​do​ ​%>
  <span id=​"favorites-count"​ class=​"ml-2"​>
  ?
  </span>
 <%​ ​end​ ​%>
 </div>

There are two odd things about this. The first is that it doesn’t actually display the favorites count; it just displays a question mark. (Which is mostly there so that it’s clear that something is happening. In actual practice, I’d probably leave it blank or put in some kind of loading spinner.) The second is that we’ve added a src argument to the turbo_frame_tag helper, which results in a src="" attribute in the eventual <turbo-frame> tag.

When a turbo-frame has a src attribute, Turbo will automatically update the contents of the frame once by calling the src url. So the page will load with the question mark and then immediately call favorites_path(count_only: true) to fetch the contents of that frame.

We need to make the following minor change in the index method to allow for a count-only display. I’m justifying putting this in the index method on the maybe-dubious grounds that the count is a kind of display of the entire set of favorites. But if you wanted to create a FavoritesCount controller, that would work too.:

 def​ ​index
 if​ params[​:count_only​]
  render(​partial: ​​"favorites/count"​)
 end
 end

That method calls a new partial if the count_only parameter is set:

 <span>Favorite Count:</span>
 <%=​ turbo_frame_tag(​"favorites-count-number"​) ​do​ ​%>
  <span id=​"favorites-count"​ class=​"ml-2"​>
 <%=​ current_user&.​favorites​&.​count​ || 0 ​%>
  </span>
 <%​ ​end​ ​%>

This partial has the same structure as the fragment from the nav partial except that it actually displays the favorites count. Critically, it has a Turbo Frame with a matching ID as the Turbo Frame in the navigation—favorites-count-number.

Also—and this is very important—this version of the tag does not have an src attribute set. If you fetch a version with an src attribute, Turbo will assume it is another lazy load, and will try to fetch it again. This can easily lead to an infinite loop where Turbo just continually fetches the same src. Ask me how I know that.

Anyway, the placeholder is loaded with the original page and then Turbo loads the real version. Then Turbo matches the Turbo Frame IDs, extracts the matching segment of the return, and replaces the placeholder version.

If you want the loading to be even lazier, adding the attribute loading=lazy will delay the turbo frame from fetching its source until the frame is actually visible. This is useful for menus or “more detail” frames that might be hidden on first page load.

To finish making this work, the update and delete turbo_frame.erb Turbo Streams also need to update this number. All both files need is the following call:

 <%=​ turbo_stream.​update​(
 "favorites-count"​,
 plain: ​current_user.​favorites​.​count
  ) ​%>

Here we’re adding a Turbo Stream update call to the DOM ID favorites-count with just the text of the new value. If you look at the earlier code snippets, you’ll see that favorites-count is just the inner span with the value, not the Turbo Frame. By using update instead of replace, we can just send the number rather than the entire HTML structure.

And again, this now works and we’ve still not written any JavaScript.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.125.219