In the app I am building, the user is meant to be redirected to the "show" page when they click on the "replies" section, which looks like this:
<%= link_to tweet_path(tweet_presenter.tweet), class: "text-decoration-none text-black replies" do %>
<%= image_tag "speech.png", size: "20x20", class: "me-1" %>
<span>Replies<span>
<% end %>
... and forms part of a view partial (_tweet.html.erb) which is enclosed in a turbo_frame that opens so: <%= turbo_frame_tag dom_id(tweet_presenter.tweet) do %>
The redirection does not occur, however. Instead, I obtain the following error message:
Uncaught (in promise) Error: the response (200) did not contain the expected <turbo-frame id=“tweet_1” and will be ignored…
I have checked that the link_to tweet_path(tweet_presenter.tweet) path is the correct one by running rails routes, where I obtain the following:
tweet GET /tweets/:id(.:format) tweets#show
The output from the console, displays that the correct tweet_id is being accessed, despite what the error message says:
Started GET "/tweets/1" for ::1 at 2023-04-10 15:53:46 -0700
Processing by TweetsController#show as HTML
Parameters: {"id"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:2:in `block in <class:ApplicationController>'
Tweet Load (0.3ms) SELECT "tweets".* FROM "tweets" WHERE "tweets"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/tweets_controller.rb:5:in `show'
Rendering layout /Users/doracobian/.rvm/gems/ruby-3.1.1/gems/turbo-rails-1.4.0/app/views/layouts/turbo_rails/frame.html.erb
Rendering tweets/show.html.erb within layouts/turbo_rails/frame
Rendered tweets/show.html.erb within layouts/turbo_rails/frame (Duration: 44.9ms | Allocations: 762)
Rendered layout /Users/doracobian/.rvm/gems/ruby-3.1.1/gems/turbo-rails-1.4.0/app/views/layouts/turbo_rails/frame.html.erb (Duration: 46.0ms | Allocations: 853)
Completed 200 OK in 52ms (Views: 46.8ms | ActiveRecord: 0.5ms | Allocations: 2490)
...and looking at the "elements" in the developer tools, I see turbo-frame#tweet_1, again, contradicting what the error message tells me.
What is going on? I'm going around in circles trying to figure this out.
For whoever takes the trouble to look into this quandary of mine, here is what the pertinent portion of routes.rb looks like:
resources :tweets, only: [:show, :create] do
resources :likes, only: [:create, :destroy]
resources :bookmarks, only: [:create, :destroy]
resources :retweets, only: [:create, :destroy]
end
... the "show" portion of the tweets_controller.rb:
def show
@tweet = Tweet.find(params[:id])
end
Very grateful for any input on this.
The frame is always going to be there, regardless of what you get as a response. If there is a frame with the same id in the response than it will be updated, if not, you will get frame missing error. To see what you actually get as a response you have to look at the network tab:
https://stackoverflow.com/a/75916578/207090
Just to make it clear, if you have this on the index page:
<!-- app/views/tweets/index.html.erb -->
<turbo-frame id="new_tweet">
<a href="/tweets/new">New tweet</a>
</turbo-frame>
Then you need the same frame on the page that you're requesting or redirecting to:
<!-- app/views/tweets/new.html.erb -->
<turbo-frame id="new_tweet">
<form action="/tweets" method="post">
<textarea name="tweet[content]"></textarea>
<input type="submit" name="commit" value="Create Tweet">
</form>
</turbo-frame>
As I understood, your turbo frame is in _tweet.html.erb partial and I don't see that partial being rendered from your show page.
Here is my tweetster clone. Create new tweets from the index page in a frame, then insert newly created tweet with turbo stream. Same thing for replies and replies on replies and it works forever.
There are also so many ways to set this up, this is just one that came to mind.
# db/migrate/20230413231722_create_tweets.rb
class CreateTweets < ActiveRecord::Migration[7.0]
def change
create_table :tweets do |t|
t.text :content
t.references :tweet
end
end
end
# app/models/tweet.rb
class Tweet < ApplicationRecord
validates :content, presence: true
has_many :replies, class_name: "Tweet", dependent: :destroy
belongs_to :tweet, optional: true
def reply?
tweet_id.present?
end
end
# config/routes.rb
resources :tweets do
get :reply, on: :member
end
You can pretty much ignore this controller, I've only added reply action and turbo_stream format to create, because after the tweet is created we're done with the frame and we have to make our great escape from it:
# app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
def index # GET /tweets
@tweets = Tweet.where(tweet_id: nil) # ignore replies
end
def new = tweet # GET /tweets/new
# show and edit are not used
def show = tweet # GET /tweets/1
def edit = tweet # GET /tweets/1/edit
def reply # GET /tweets/1/reply
render partial: "form", locals: {tweet: tweet.replies.new}
end
def create # POST /tweets
respond_to do |f|
if tweet(tweet_params).save
# NOTE: render `create.turbo_stream.erb` where we'll handle some
# things regarding turbo_frame
f.turbo_stream
f.html { redirect_to tweet_url(tweet), notice: "Saved." }
else
f.html { render tweet.new_record? ? :new : :edit, status: 422 }
end
end
end
alias_method :update, :create # PATCH/PUT /tweets/1
def destroy # DELETE /tweets/1
tweet.destroy
respond_to do |f|
f.turbo_stream { render turbo_stream: turbo_stream.remove(tweet) }
f.html { redirect_to tweets_url, notice: "Destroyed." }
end
end
private
def tweet attributes = {}
@tweet ||= begin
model = params[:id] ? Tweet.find(params[:id]) : Tweet.new
model.attributes = attributes
model
end
end
def tweet_params = params.require(:tweet).permit!
end
# app/views/tweets/index.html.erb
<%= link_to "New tweet", new_tweet_path, data: {turbo_frame: :new_tweet} %>
<%= turbo_frame_tag :new_tweet %>
<%= tag.div id: :tweets, class: "grid gap-4 mt-4" do %>
<%= render @tweets.order(id: :desc) %>
<% end %>
# app/views/tweets/new.html.erb
<%= render "form", tweet: @tweet %>
# app/views/tweets/_form.html.erb
<%# NOTE: figure out correct turbo frame id for replies and tweets %>
<% frame_id = tweet.reply? ? dom_id(tweet.tweet, :new_reply) : dom_id(tweet) %>
<%= turbo_frame_tag frame_id do %>
<%= form_with model: tweet, class: {"ml-8": tweet.reply?} do |f| %>
<%= tag.div safe_join(f.object.errors.full_messages, tag.br), class: "text-red-500" if f.object.errors.any? %>
<%= f.hidden_field :tweet_id %>
<%= f.text_area :content, placeholder: (tweet.reply? ? "reply..." : "new tweet..."), class: "rounded-xl w-full" %>
<%= f.button "Save", class: "font-bold" %>
<% end %>
<% end %>
# app/views/tweets/_tweet.html.erb
<%= tag.div id: dom_id(tweet), class: ["grid gap-4", {"ml-8": tweet.reply?}] do %>
<%= tag.div class: "flex justify-between rounded-xl shadow border p-4" do %>
<%= tweet.content %>
<%= tag.div class: "flex gap-2" do %>
<%= link_to "reply", reply_tweet_path(tweet), data: {turbo_frame: dom_id(tweet, :new_reply)} %>
<%= button_to "delete", tweet_path(tweet), method: :delete, class: "hover:text-red-500" %>
<% end %>
<% end %>
<%= turbo_frame_tag dom_id(tweet, :new_reply), class: "contents" %>
<%= tag.div id: dom_id(tweet, :replies) do %>
<%= render tweet.replies %>
<% end %>
<% end %>
# i'm only using tag.div for syntax highlight for SO (erb + html tags don't mix well)
This is probably the main part where you need to make things happen outside of the frame, maybe replace a frame with a different frame, maybe append a flash alert somewhere, etc:
# app/views/tweets/create.turbo_stream.erb
<%# there is probably a way to make it the same for tweets and replies %>
<% if @tweet.reply? %>
<% parent_tweet = @tweet.tweet %>
<%= turbo_stream.update dom_id(parent_tweet, :new_reply) %>
<%= turbo_stream.append dom_id(parent_tweet, :replies), @tweet %>
<% else %>
<#% this v is for tweets and same ^ for replies %>
<%# NOTE: this `update` will remove content from turbo_frame %>
<%# but you can add whatever updates you need here %>
<%= turbo_stream.update :new_tweet %>
<%# NOTE: this will add new tweet to `<div id="tweets">` %>
<%= turbo_stream.prepend :tweets, @tweet %>
<% end %>
If you want to "just" redirect out of the frame:
https://stackoverflow.com/a/75750578/207090
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With