2

I'm working with Hotwire the last month or two and trying to understand how to reload a specific turbo-frame after it renders the first time.

Let's say I have a simple rails controller for user searches. If it has a param of name, return users matching that name, if not, return all users.

class UserSearchController
  def show
    return User.where(name: params[:name]) if params[:name].present?

    return User.all
  end
end

I have a Turboframe hooked up that is correctly lazy loading all users in the DOM. What I'm trying to figure out is how I can update that Turboframe and redraw the frame. I've played around a bit with Stimulus and trying to set the src to the endpoint, that's the suggestions I've seen in other posts:

  reload() {
    const {src: src} = this;
    this.src = null;
    this.src = src;
  }

That doesn't seem to work though, it doesn't redraw the turboframe. I can see it's making the request though to the rails backend.

Would anyone be able to point me in the direction of reloading/redrawing a frame after a page loads? I'm not sure if I'm completely off or am in the right ballpark.

D1D2D3D4D5
  • 101
  • 6

1 Answers1

3

You need some instance variables in that show action or render locals: {.., otherwise, you return into nothing. In stimulus controller, this refers to controller instance, without seeing the rest of it, it looks like it does nothing.

Copy this to any page and click away to reload:

<%= turbo_frame_tag :reloadable, style: "display:none;" do %>
  <%= request.uuid %>
<% end %>

# clicking this frame will load that ^ frame, because it's first.
<%= turbo_frame_tag :reloadable, src: request.url, onclick: "reload()" %>

Duplicate ids, I know. Put it on separate pages, not duplicate anymore.


Because onclick isn't a cool thing to do:

// app/javascript/controllers/hello_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  reload() {
    this.element.reload();

    // this works as well
    // this.element.src = this.element.src;

    // not sure if you would ever need to do it this way
    // this.element.removeAttribute("complete");
  }
}
<%= turbo_frame_tag(:reloadable, style: "display:none;") { request.uuid } %>

<%= turbo_frame_tag :reloadable, src: request.url,
  data: {
    controller: "hello",
    action: "click->hello#reload"
  }
%>

https://turbo.hotwired.dev/reference/frames#functions


As for the search form:

# app/views/any/where.html.erb

<%= form_with url: "/users", method: :get,
  data: {turbo_frame: :users} do |f| %>

  <%= f.search_field :name, placeholder: "search by name" %>
  <%= f.button "Search" %>
<% end %>

# load this frame from `src`. load it again from the form submission response.
<%= turbo_frame_tag :users, src: "/users", target: :_top %>
# app/views/users/index.html.erb

<%
  scope = User.all
  scope = scope.where(name: params[:name]) if params[:name].present?
  @users = scope
%>

<%= turbo_frame_tag :users, target: :_top do %>
  <% @users.each do |user| %>
    <%= user.name %>
  <% end %>
<% end %>

https://turbo.hotwired.dev/handbook/frames#targeting-navigation-into-or-out-of-a-frame

Alex
  • 16,409
  • 6
  • 40
  • 56
  • Thanks. I should've been clearer with the controller example. I have it returning instance variables already. I just wanted to make sure I showed in my question it could return mixed data. I've confirmed the endpoint works. It loads on initial page load fine. It's just that when I try and do like you mention, "reload" or "set src", the frame doesn't reload. From what you've provided, it sounds like that approach should work though. Like I mentioned, I can see the request coming in to Rails too, it just doesn't reload the frame. – D1D2D3D4D5 Apr 25 '23 at 03:45
  • Appreciate the response @alex – D1D2D3D4D5 Apr 25 '23 at 03:47
  • @D1D2D3D4D5 if response comes in, you need to check the browser console for any errors. also are sure response is actually different? if it is the same response, it will look like nothing happened. – Alex Apr 25 '23 at 03:49
  • no errors and I can see the request hitting rails and processing OK, just no DOM update. Response returned is different too, that was the reason I mentioned showing controller returning mixed data :) I'll have another crack tomorrow with fresh eyes and how it goes. I can then provide more details, or, resolve the thread with an update. FWIW, I'm lazy loading these frames. I read there can be differences with setting the src, but I don't think that should stop the frames from re-rendering altogether. – D1D2D3D4D5 Apr 25 '23 at 04:52
  • OK, so I got a bit further with this and not exactly sure what the issue is. I can confirm that setting src is re-rendering the frame (yay). The issue is that when I append query params, it's losing them when it hits rails. I can confirm that there's no issue with the endpoint accepting the query params in general (tested in Postman). Any ideas why setting the src of a Turboframe with query params would lose them when submitted to rails? I can see the inbound request on rails side doesn't have them so it's just re-rendering the first results over and over. – D1D2D3D4D5 Apr 25 '23 at 17:46
  • 1
    Resolved. The last problem was my end. I wasn't building the request correctly from my app before sending the request for data to the datastore. Cheers, appreciate the help. – D1D2D3D4D5 Apr 25 '23 at 18:38