rails turbo_stream is not working on a rails 7 application - ruby-on-rails-7

I am trying to submit a form and return a turbo_stream on a rails 7.0 application.
In my form I have the following.
<%= form_with url: "/request_trial", method: :post, format: :turbo_stream do |form| %>
<%= form.label :name, I18n.t('marketing.form.name'), class: 'form-label required' %>
<%= form.text_field :name, class:"form-control", required: true %>
<%= form.submit I18n.t('marketing.form.send'), class: 'btn btn-trial' %>
<% end %>
In my controller I have the following
respond_to do |format|
format.turbo_stream do |format|
render turbo_stream: turbo_stream.replace(:'marketing-request-trial-form',
partial: 'marketing/request_trial')
end
end
This gives me an error ActionController::UnknownFormat
Although I have a format specify in my form, it seems that format is html when I submit the form.
I can see where this is coming from, on my request headers I have the following
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
I need to add on the request header the text/vnd.turbo-stream.html type, how can I do that?

you need emphasis the format type
format.turbo_stream do
render turbo_stream: turbo_stream.replace(#article, partial: 'welcome/form')
end
you can reference Rails Completed 406 Not Acceptable in 64ms
welcome_controller.rb
class WelcomeController < ApplicationController
def index
#article = Article.first
end
def update_article
#article = Article.find(article_params[:id])
respond_to do |format|
if #article.update(article_params)
format.turbo_stream do
render turbo_stream: turbo_stream.replace(#article, partial: 'welcome/form')
end
end
end
end
def article_params
params.require(:article).permit(:id,:title, :body)
end
end
index.html.erb
<h1>Welcome ! This is a tutorial about Rails forms</h1>
<%= turbo_frame_tag dom_id(#article) do %>
<%= form_with model: #article, url: update_article_path, method: :post, format: :turbo_stream do |form| %>
<%= form.text_field :id %>
<%= form.text_field :title %>
<%= form.text_field :body %>
<%= form.submit "Update" %>
<% end %>
<% end %>
_form.html.erb
<%= turbo_frame_tag dom_id(#article) do %>
<h1>New Book</h1>
<div class="card card-body">
<label>title</label>
<input value="<%= #article.title %>">
</div>
<% end %>
schema.rb
ActiveRecord::Schema[7.0].define(version: 2022_02_16_084944) do
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end

The reason you got ActionController::UnknownFormat exception is that you only respond turbo_stream format, but the actual format is HTML(You didn't pass the correct format).
Solution 1
format: :turbo_stream is assumed to be passed as an argument of URL helpers rather than form helpers. For example:
some_url_helper_path(format: :turbo_stream)
In your literal URL case should be:
<%= form_with url: "/request_trial.turbo_stream", method: :post do |form| %>
Solution 2
Solution 1 can make controller respond turbo_stream correctly. But due to the bug, you might see turbo_stream as plain text instead of replacing views. Before a new version of Turbo js release, you can add a hidden field to forms manually:
<input type="hidden" name="format" value="turbo_stream">

Related

Ruby On Rails 7.0 invalid? set true in console but not in view

I´m trying to avoid to update an empty name for #post.
I´m beginner in RoR and I don´t understand why in terminal I got #post.invalid? => true but in my view edit.html.erb #post.invalid? => false
posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, :only => [:edit, :show, :update, :destroy]
def index
#posts = Post.all
respond_to do |format|
format.html
format.json { render json: #posts }
end
end
def show
respond_to do |format|
format.html
format.json { render json: #post }
end
end
def edit
end
def update
if #post.update(post_params)
redirect_to posts_path, success: "Post updated"
else
puts #post.invalid? # write true
render 'edit'
end
end
def new
#post = Post.new
end
def create
post = Post.create(post_params)
redirect_to post_path(post.id), success: "Post created"
end
def destroy
#post.destroy
redirect_to posts_path, success: "Post deleted"
end
private
def post_params
params.require(:post).permit(:name, :content)
end
def set_post
#post = Post.find(params[:id])
end
end
post.rb
class Post < ApplicationRecord
validates :name, presence: true
def as_json(options = nil)
super(only: [:name, :id, :created_at] )
end
end
edit.html.erb
<h1>Editer l´article</h1>
<%= #post.invalid? %> <!-- write false -->
<% if #post.invalid? %> <!-- return false -->
<div class="alert alert-danger">
<% #post.errors.full_messages.each do |message| %>
<%= message %>
<% end %>
</div>
<% end %>
<%= form_for #post do |f| %>
<div class="form-group">
<label>Titre de l´article</label>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<label>Contenu de l´article</label>
<%= f.text_area :content, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.submit "Modifier l´article", class: 'btn btn-primary' %>
</div>
<% end %>
I´m confused, someone has got an idea ?
The methods valid? and invalid? all keep running the validation methods every time they are called, and therefore potentially change the state of the model.
If you want to just check for validity when validation has already been run, you should instead use #post.errors.present? or #post.errors.blank? which will never change the status, only read existing errors (that were added in your case when the call to update failed.
Additionally (even if it´s not the case here) calling valid? and invalid? without a context will clear out errors that had been added with validations like validate ..., on: :update.
The solution I found was a combination of #rewritten´s answer and this one.
So...
<h1>Editer l´article</h1>
<% if #post.errors.present? %>
<div class="alert alert-danger">
<% #post.errors.full_messages.each do |message| %>
<%= message %>
<% end %>
</div>
<% end %>
<%= form_for #post, data: { turbo: false} do |f| %>
<div class="form-group">
<label>Titre de l´article</label>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<label>Contenu de l´article</label>
<%= f.text_area :content, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.submit "Modifier l´article", class: 'btn btn-primary' %>
</div>
<% end %>

ActiveRecord Model object not getting saved

I have a produce model that uses ActiveStorage has_one_attached. I am trying to save the object after form submission but it doesn't save and the logs only say rollback with no helpful error messages.
Form:
<%= form_with(model: #produce, local: true) do |f| %>
<div class="form-group">
<label for="">Select Category</label>
<%= f.select(:category_id, [],{}, {:class => "browser-default custom-select"}) do %>
<% Category.all.each do |category| %>
<%= content_tag(:option, category.name, value: category.id ) %>
<% end %>
<% end %>
</div>
<div class="form-group">
<%= f.text_field :name, class: "form-control", placeholder: "Name"%>
</div>
<div class="form-group">
<%= f.label :prod_image, "Upload Product Image"%>
<%= f.file_field :prod_image, class: "form-control-file"%>
</div>
<div class="form-group">
<%= f.submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
Controller Action:
def create
#produce = Produce.new(prod_params)
bybug
if #produce.save
flash[:notice] = "Object successfully created"
redirect_to produces_path
else
flash[:alert] = "Something went wrong"
render 'new'
end
end
private
def prod_params
params.require(:produce).permit([:name, :category_id, :prod_image])
end
Model:
class Produce < ApplicationRecord
belongs_to :category
has_one_attached :prod_image
end
Logs:
Started POST "/produces" for ::1 at 2020-05-14 01:55:47 -0500
Processing by ProducesController#create as HTML
Parameters: {"authenticity_token"=>"/IKY+MD350d6xSn5vkx8ZdIX/iGCp3sOokHQwkJM+tGX326sUjLKsx9YuWI+ZqDRsqMV0g4V7JbR4ilOTJE/dw==", "produce"=>{"category_id"=>"2", "name"=>"Cabbage", "prod_image"=>#<ActionDispatch::Http::UploadedFile:0x0000000006b1bc68 #tempfile=#<Tempfile:C:/Users/DEVELO~1/AppData/Local/Temp/RackMultipart20200514-18284-1yh47qh.jpg>, #original_filename="cabbage.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"produce[prod_image]\"; filename=\"cabbage.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Submit"}
(0.4ms) BEGIN
↳ app/controllers/produces_controller.rb:10:in `create'
Category Load (0.6ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/controllers/produces_controller.rb:10:in `create'
(0.4ms) ROLLBACK
↳ app/controllers/produces_controller.rb:10:in `create'
Rendering produces/new.html.erb within layouts/application
Where am I going wrong?

undefined method `accept_nested_attributes_for' for #<Class:0x007fc5844fd868>

I am using rails 4. I am trying add a nested form of address in user but its throwing error: undefined method `accept_nested_attributes_for' for #
User.rb
has_many :addresses
accept_nested_attributes_for :addresses, :allow_destroy => true
Address.rb
belongs_to :user
form template
<%= simple_form_for #user do |f| %>
<%= f.input :name, :hint =>(t "user.name_eg"), :label =>(t "user.name") %>
<% f.fields_for :addresses do |addr| %>
<p>
<div>
<%= addr.text_field :address %>
</div>
</p>
<% end %>
<% end %>
controller
#user = User.new
#user.addresses.build
Please help what am I missing. Thanks
The method is "accepts_nested_attributes_for". You can read about it here or here

Invalid date hides form button

I am having a problem where I have a date picker form. I have found an error where if I enter an invalid date for both forms it hides the "submit" button I have. How can I make the forms dynamic so that they move left as well. The end goal is that my "submit" button will not be hidden. Here are the 2 screenshots of before and after 2 invalid dates (completely hidden submit button).
no errors
errors. and submit button is gone
Heres's the code I have so far:
<div class="date-picker text-center x-small-heading">
<%= form_tag '', method: :get, enforce_utf8: false do %>
<%= label_tag 'start_date', t('date_picker.from'), class: 'x-small-heading' %>
datetime_picker(datepicker: { value: #date_range.begin.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
start_date: date_picker_min.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
end_date: date_picker_max.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
name: 'start_date' })
%>
<%= label_tag 'end_date', t('date_picker.to'), class: 'x-small-heading' %>
<%= datetime_picker(datepicker: { value: #date_range.end.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
start_date: date_picker_min.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
end_date: date_picker_max.strftime(ApplicationController::DATE_PICKER_DATE_FORMAT),
name: 'end_date' })
%>
<%= submit_tag t('date_picker.submit'), name:nil %>
<% end %>
<% if defined?(show_quick_picks) && show_quick_picks %>
<%= select_tag 'quick-picks', options_for_select(date_quick_pick_options(quick_picks_type)), include_blank: t('quick_picks.title') %>
<% end %>
</div>

Improper form validation

I'm running into a strange issue. Here is the controller code regarding the flash:
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_path, notice: "Signed in!"
else
flash.now[:error] = 'Your email or password is incorrect!'
render "new"
end
end
Here is the associated view code:
<% if flash.now[:error] %>
<%= label_tag :email %>
<%= text_field_tag :email, nil, class: "error"%>
<%= label_tag :password %>
<%= password_field_tag :password, nil, class: "error" %>
<% else %>
<%= label_tag :email %>
<%= text_field_tag :email%>
<%= label_tag :password %>
<%= password_field_tag :password%>
<% end %>
The error class should result in the input fields turning red. However, they currently briefly flash red and then the page is rendered. What can I be doing better here?
Thanks
From the docs:
When you need to pass an object to the current action, you use now, and your object will vanish when the current action is done.
Entries set via now are accessed the same way as standard entries: flash['my-key'].
(see http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html#method-i-now)
Try removing the '.now' in your view and instead do
<% if flash[:error] %>

Resources