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] %>
Related
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">
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 %>
I've got hotwire/turbo wired up correctly to do crud operations on a single model on one place of my page, but I'd like to update the same model at the same time in a different location on my page as well. I thought I could just set up two streams but it doesn't seem to work.
Specifying a target does work for create action depending on how I name the target, but not for update and destroy. This is what I think should work but doesn't:
----location 1 ("creatures" stream)----
<div id="creatures">
<%= turbo_stream_from "creatures" %>
<%= turbo_frame_tag "creatures" do %>
<div>
<% #creatures.each do |creature| %>
<div>
<%= render "creatures/creature", creature: creature %>
</div>
<% end %>
</div>
<% end %>
</div>
----location 2 ("creatures_main" stream)----
<%= turbo_stream_from "creatures_main" %>
<%= turbo_frame_tag "creatures_main" do %>
<% #creatures.each do |creature| %>
<div>
<%= render "creatures/creature", creature: creature %>
</div>
<% end %>
<% end %>
---- common _creature.html.erb partial ----
<%= turbo_frame_tag dom_id(creature) do %>
<%= link_to creature.name, "#" %>
<% end %>
---- creature.rb ----
class Creature < ApplicationRecord
validates :name, presence: true
after_create_commit {
broadcast_append_to "creatures"
broadcast_append_to "creatures_main"
}
after_update_commit {
broadcast_replace_to "creatures"
broadcast_replace_to "creatures_main"
}
after_destroy_commit {
broadcast_remove_to "creatures"
broadcast_remove_to "creatures_main"
}
end
What happen when I have two calls in my model is that the create action puts the newly created creature in location 1 twice, only 1 of the two are updated,but both are destroyed correctly regardless of where on the page they are.
creatures and creatures_main would be the stream name. What you're looking for is the target and partial to control where the stream would look to update your data, and which partial it would use to update.
You can try:
after_create_commit -> {
# target here is the ID of the outer div, where data would be appended to
broadcast_append_to "creatures", target: "creatures"`
broadcast_append_to "creatures_main", target: "creatures_main"
}
after_update_commit {
# target here is the ID of each of the creature div
broadcast_replace_to "creatures", target: "creature_#{id}"
broadcast_replace_to "creatures_main", target: "creature_main_#{id}"
}
after_destroy_commit {
broadcast_remove_to "creatures"
broadcast_remove_to "creatures_main"
}
<%= turbo_frame_tag "creature_#{creature.id}" do %>
<%= link_to creature.name, "#" %>
<% end %>
<%= turbo_frame_tag "creature_main_#{creature.id}" do %>
<%= link_to creature.name, "#" %>
<% end %>
Of course this means you might have to use 2 different partials if you have the turbo_frame_tag inside the partial. You can do so like this:
after_update_commit {
# target here is the ID of each of the creature div
broadcast_replace_to "creatures", target: "creature_#{id}", partial: "creatures/creature", locals: {creature: self}
broadcast_replace_to "creatures_main", target: "creature_main_#{id}", partial: "creatures/creature_main", locals: {creature: self}
}
Btw, you should use the _later version of those methods. Also use render collection for easier reading.
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
A Persona has persona_id of either 1, 2 or 3. I want to assign a class type of either persona-1-button, persona-2-button or persona-3-button inside the embedded ruby. The following code is not working and I don't know why:
<% current_user.personas.each do |persona| %>
<% foo = persona.persona_id.to_s %>
<% bar = "persona-" + foo + "-button" %>
<%= link_to "Persona", persona_path(persona), class: "btn btn-medium bar" %>
<% end %>
I didn't do it the follwoing way because it seems you can't have a <%=%> inside a <%=%>:
<% current_user.personas.each do |persona| %>
<%= link_to "Persona", persona_path(persona), class: "btn btn-medium persona<%=persona.persona_id%>button" %>
<% end %>
You have it almost correct already.
The thing you need to realise is that when you're inside the <%/%> tags, you're in a Ruby context. That means, that the "..." creates a String inside which you can use regular Ruby string interpolation, like this:
<%= link_to "Persona", persona_path(persona), class: "btn btn-medium #{bar}" %>
You're placing a variable in Ruby context where you are bound by the rules of Ruby, not ERB. And in Ruby it's done using string interpolation:
<%= link_to "Persona", persona_path(persona), class: "btn btn-medium #{bar}" %>